一般继承(无虚函数覆盖)
- 虚函数按照其声明顺序放于表中
- 父类的虚函数在子类的虚函数前面
一般继承(有虚函数覆盖)
- 覆盖的函数被放到了虚表中原来父类虚函数的位置
- 没有被覆盖的函数依旧
多重继承(无虚函数覆盖)
- 每个父类都有自己的虚表
- 子类的成员函数被放到了第一个父类的表中(所谓的第一个父类是按照声明顺序来判断的)
多重继承(有虚函数覆盖)
// 待补充
C++对象模型之内存布局(1)
C++对象模型之内存布局(2)
- 多继承的内存布局和单继承和多重继承不一样,子类继承一个父类,子类就有一个虚函数表,当子类继承两个父类时,子类就有两个虚函数表
- 而且子类自己定义的虚函数,放在了第一个继承类的虚函数表里
//多继承的内存布局
#include "stdafx.h"
#include <iostream>
using namespace std;
class A
{
public:
A(int a1 = 0, int a2 = 0){
}
virtual void A1() { cout << "A::A1()" << endl; }
virtual void A2() { cout << "A::A2()" << endl; }
protected:
int a1;
int a2;
};
class B {
public:
B(int b1 = 0){
}
virtual void B1(){ cout << "B::B1()" << endl; }
virtual void B2() { cout << "B::B2()" << endl; }
protected:
int b1;
};
class C :public A, public B{
public:
C(int a1 = 0, int a2 = 0, int b1 = 0, int c1 = 0){
}
virtual void C1(){ cout << "C::C1()" << endl; }
virtual void A2() { cout << "C::A2()" << endl; }
virtual void B2() { cout << "C::B2()" << endl; }
protected:
int c1;
};
typedef void(*pfun)();
int _tmain(int argc, _TCHAR* argv[])
{
C *pc = new C;
pfun fun = NULL;
cout << "virtual table a" <<endl;
for (int i = 0; i < 3;i++)
{
fun = (pfun)*((long *)*(long*)pc + i);
fun();
}
cout << "virtual table B" << endl;
int *p = (int *)pc + 3;
for (int i = 0; i < 2; i++)
{
fun = (pfun)*((long *)*(long*)p + i);
fun();
}
cout << *((long *)*(long*)p + 2) << endl;
return 0;
}
C++ 虚函数
C++虚函数实现原理
从内存布局看C++虚继承的实现原理
C++中虚继承的作用及底层实现原理
- 虚继承之所以能够实现在多重派生子类中只保存一份共有基类的拷贝
- 实际上,vbptr指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚基类表(virtual table),虚表中记录了虚基类与本类的偏移地址
- 通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。
- 在这个例子中,类B中的vbptr指向了虚表D::$vbtable@B@,虚表表明公共基类A的成员变量dataA距离类B开始处的位移为20,这样就找到了成员变量dataA,而虚继承也不用像普通多继承那样维持着公共基类的两份同样的拷贝,节省了存储空间。