先来看一段代码:
#include <bits/stdc++.h>
using namespace std;
class base1 {
public:
virtual void fun() { cout << "base1" << endl; }
virtual ~base1() {};
};
class base2 {
public:
virtual void fun() { cout << "base2" << endl; }
virtual ~base2() {};
};
class derived : public base1, public base2 {
public:
virtual void fun() { cout << "derived" << endl; }
virtual ~derived() {};
};
int main() {
derived* ptr1 = new derived;
return 0;
}
问:derived::fun()
会覆盖那个基类的同名函数?
答:会覆盖所有基类同名函数。
解析
借助分析工具,获取类布局情况,关于类布局的获取可以看这篇文章。C++虚函数表底层结构
Vtable for base1
base1::_ZTV5base1: 5 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI5base1)
16 (int (*)(...))base1::fun
24 (int (*)(...))base1::~base1
32 (int (*)(...))base1::~base1
Vtable for base2
base2::_ZTV5base2: 5 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI5base2)
16 (int (*)(...))base2::fun
24 (int (*)(...))base2::~base2
32 (int (*)(...))base2::~base2
Vtable for derived
derived::_ZTV7derived: 10 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI7derived) // derived
16 (int (*)(...))derived::fun
24 (int (*)(...))derived::~derived
32 (int (*)(...))derived::~derived
40 (int (*)(...))-8
48 (int (*)(...))(& _ZTI7derived) // derived
56 (int (*)(...))derived::_ZThn8_N7derived3funEv // derived::fun()
64 (int (*)(...))derived::_ZThn8_N7derivedD1Ev // derived::~derived()
72 (int (*)(...))derived::_ZThn8_N7derivedD0Ev // derived::~derived()
Class derived
size=16 align=8
base size=16 base align=8
derived (0x0xb8cf9a0) 0
vptr=((& derived::_ZTV7derived) + 16) // 第21行
base1 (0x0xbd97180) 0 nearly-empty
primary-for derived (0x0xb8cf9a0)
base2 (0x0xbd971e0) 8 nearly-empty
vptr=((& derived::_ZTV7derived) + 56) // 第26行
从上可以看出,derived
类有两个虚表指针,每个虚表指针指向各自的虚表,同时可以看到derived::fun()
覆盖了base1::fun()
和base2::fun()
。
关于符号名称的解码,参考这篇文章:GCC生成的符号名称的还原-demangle方法
结论:
在 C++ 中,当一个派生类继承多个基类时,如果派生类中有一个函数与基类中的函数具有相同的签名,那么这个函数将会覆盖所有基类中相同签名的虚函数。