众所周知,C++虚函数是一大难点,也是面试过程当中必考部分。这次,从虚函数的相关概念、虚函数表、纯虚函数、再到虚继承等等跟虚函数相关部分,作一个比较细致的整理和复习。面试
cpp //强行调用基类中定义的函数版本而无论baseP的动态类型究竟是什么 double price = basePtr->Base::net_price();
cpp //Base 声明了纯虚函数,而 Derive将覆盖该函数 Base b; //错误,不能定义Base的对象 Derive d; //正确,Derive中没有纯虚函数
基类定义以下所示:
```cpp
class Base{
public:
Base()
:a(0), b(0), c('\0'){}函数
virtual void fun1(){ cout << "Base::fun1()" << endl; } virtual void fun2(){ cout << "Base::fun2()" << endl; }
private:
int a;
double b;
char c;
};
```
类Base对象其内存布局方式为:
布局
考虑继承的状况,以下所示:
```cpp
class Derive : public Base{
public:
Derive()
:Base(),d(0), f(0){}学习
virtual void fun1(){ cout << "Derive::fun1()" << endl; } virtual void fun3(){ cout << "Derive::fun3()" << endl; }
private:
int d;
float f;
};
```
类Derive对象其内存布局以下所示:
.net
其实Derive对象的内存布局是能够这样理解,可是也不是很准确。
如上所示,在Derive的定义中,我从新实现了Base的fun1(),直接继承了Base::fun2(),再新定义了 Derive::fun3()
经过调试,即上面的右图发现,在Derive的对象中,可以看到的虚函数表是从Base继承而来的,其中里面覆写fun1(),继承了fun2(),可是并无fun3()的函数指针。因此按照上边的左图,给出内存布局的话,可能会有一些误导。指针
由上可知,派生类若是没有定义新的虚函数,则直接继承虚类的虚函数表,并在其中作相应修改。若是定义了新的虚函数,不止要继承虚类的,还要维护本身的。
因此上面的Derive的内存布局的另外一种状况多是:
调试
下面给出一个多重继承的讨论状况:
```cpp
class Base1{
public:
Base1()
{}code
virtual void fun1(){ cout << "Base1::fun1()" << endl; } virtual void fun2(){ cout << "Base1::fun2()" << endl; }
};对象
class Base2{
public:
Base2(){}blog
virtual void fun3(){ cout << "Base2::fun3()" << endl; } virtual void fun4(){ cout << "Base2::fun4()" << endl; }
};
class Derive : public Base1, public Base2(){
public:
Derive()
:Base1(), Base2() {}
virtual void fun2(){ cout << "Derive::fun2()" << endl; } virtual void fun3(){ cout << "Derive::fun3()" << endl; } virtual void fun5(){ cout << "Derive::fun5()" << endl; }
}
```
Derive的对象内存布局以下:
这是篇好文章C++ 多继承和虚继承的内存布局,虽然不是很懂,可是确实有帮助。下面在给出一些相关概念。
:C++ 对象的内存布局(下)关于虚拟继承的例子部从这篇文章学习,推荐。
很明显: sizeof(Base) = 8
缘由:带有虚函数的类具备虚函数指针,而后再加上int
乍一看 sizeof(Base) = 16, 其实应该是 sizeof(Base) = 24
为何呢, 由于前面关于字节对齐中,提到过 类的隐藏对象不能影响其后的数据成员的对齐,因此通常隐藏对象都是最大对齐字节的整数倍。此时 最大对齐为8,因此 虚函数表指针占4个字节,但须要填充4个。而后 int 占 4 个,再填充 4 个,最后double占8个。一共24个。
class A {
int a;
virtual ~A(){}
};
class B:virtual public A{
virtual void funB(){}
};
class C:virtual public A{
virtual void funC(){}
};
class D:public B,public C{
virtual void funD(){}
};
sizeof(A) = 8
sizeof(B) = 12
sizeof(C) = 12
sizeof(D) = 16
A 中是虚函数指针 + int
B、C 虚继承A,大小为 A + 指向虚基类的指针,B、C虽然新定义了虚函数,可是共享A中的虚函数指针。
D 因为是普通继承 B、C,可是因为 B 、C是虚继承,因此D中保留A的一个副本。因此大小为 A + B指向虚基类的指针 + C指向虚基类的指针
```
最后给出一个上面讨论 2 的具体实例。在VS2013下查看内存布局以下:
上图中没有搞懂的部分,应该是随机数,系统随机的。不用管。