当类出现继承时,可以分为两种情况:
1. 类中没有虚函数
2. 类中存在虚函数(当类中存在虚函数时,对象中会引入一个虚函数表(为一个指针),占四个字节)
此时,问题便涉及到类中虚函数的继承与覆盖问题:
- a. 类对象中,各个变量及成员的访问法则:
class Base
{
public:
int a;
char d;
virtual void f(void) { cout << "Base::f" <<endl; }
virtual void g(void) { cout << "Base::g" << endl; }
virtual void h(void) { cout << "Base::h" << endl; }
};
typedef void (*Func)(void);
Base b;
b.a = 1;
b.d = 'd';
Func pfun = NULL;
cout << "虚函数表的地址: " << &b <<" " << endl;
cout << "虚函数表--第一个函数的地址: " << (int*)*(int*)(&b) + 0 << endl;
cout << "其他元素的地址如下:" << endl;
cout << "a: " << (int*)(&b) + 1 <<" "<< *((int*)(&b) + 1) << endl;
cout << "d: " << ((int*)(&b) + 2) <<" "<< *(char*)((int*)(&b) + 2) << endl;
pfun = ((Func)*(int*)*(int*)(&b));
pfun();
通过上述代码验证,&b 操作取得了虚函数表的地址,对象中成员的地址以及虚函数的存放如下图所示:
* 上图所述的虚函数的地址本质上为对应虚函数放置在虚函数表中的位置!故而在内存上只相差一个指针的size!
- b. 由 base1 派生 BaseDer
class BaseDer :public Base{public:virtual void f(void) { cout << "BaserDer::f" << endl; }virtual void i(void) { cout << "BaseDer::i" << endl; }};
BaseDer bd; cout << "虚函数表的地址: " << &bd << endl; cout << "虚函数表--第一个函数的地址: " << (int*)*(int*)(&bd) << endl; cout << "虚函数表--第四个函数的地址: " << (int*)*(int*)(&bd + 3) << endl; pfun = (Func)*((int*)*(int*)(&bd)); pfun(); pfun = (Func)*((int*)*(int*)(&bd) + 3); pfun();
此时,创建对象 BaseDer bd。那么,对于对象 bd 来说,其虚函数表发生了什么变化?
经过继承覆盖之后,子类的虚函数直接覆盖父类的虚函数,剩余的新添加的虚函数便续接在父类虚函数表之后。
- c. 子类 BaseDer2 继承于多各类 base1,base2
此时,BaseDer2 中有两个虚函数表(按照继承的顺序排布,此处与验证的有些出入,原因待查找),BaseDer2 中新 增加的虚函数则续接在继承的 第一个父类 之后。
class BaseDer2 :public Base, public Base2{public:virtual void f(void) { cout << "BaseDer2::f" << endl; }virtual void f2(void) { cout << "BaseDer2::f2" << endl; }
virtual void add(void){ cout << "BaseDer2::add" << endl; }};BaseDer2 bd2;bd2.a = 2;BaseDer2 bd2;bd2.a = 2;cout << "继承后的虚函数表:" << &bd2 << " " << (int*)(&bd2) << endl;cout << "继承后的虚函数表中的地址:" << endl;pfun = (Func)*((int*)*(int*)(&bd2) + 0);pfun();pfun = (Func)*((int*)*(int*)(&bd2) + 1);pfun();pfun = (Func)*((int*)*(int*)(&bd2) + 2);pfun();pfun = (Func)*((int*)*(int*)(&bd2) + 3);pfun();cout << *((int*)(&bd2) + 1) << endl;
pfun = (Func)*((int*)*((int*)(&bd2) + 1));pfun();
理论上由两个 父类 继承而来,此处的 子类 应该有两个 顺序排布的虚函数表,但是对“第二个虚函数表”进行访问时,发现访问到的数据是 类中的成员变量,并未访问到第二个虚函数表!!!此处没有弄清楚原因所在!
使用父类指针指向子类时,指针指向的是其对应虚函数表的地址!!!
由多个类派生出来的子类,子类中成员于内存中的排布顺序:
Father : 虚函数表f,成员变量 a;
Mother: 虚函数表m,成员变量b;
Son: 虚函数表f;
成员变量a;
虚函数表m;
成员变量b;