C++ 多重继承的实现
都说多重继承有很多缺点,但我一直搞不清楚是指的哪些。今天仔细研究了C++中多重继承的实现机制,发现其中的巧妙之处。先看代码:
class G
{
public:
int i;
virtual void T(){};
virtual void H(){};
};
class A:public G
{
public:
A()
{
i=1;
}
virtual void T(){cout << "A::T/n"<<i;}
};
class B:public G
{
public:
public:
B()
{
i=2;
}
virtual void T(){cout << "B::T/n"<<i;}
virtual void H(){};
};
class C:public B,A
{
virtual void T(){cout << "C::T/n";}
virtual void H(){cout << "C::H/n";};
};
void fun()
{
cout << "SB/n";
}
int add[10]={(int)&fun};
int d[10]={(int)&add,4,5,6,7,8,9};
int _tmain(int argc, _TCHAR* argv[])
{
C u;
C* i=&u;
G* a=(A*)&u;
G* b=(B*)&u; //此处观察局部变量
}
总结如下:
1、 当一个类有多个父类时,每个父类在内存中依次排列,然后该类自己的成员。
2、 每一个父类的镜像中,都包含有独立的虚函数表
3、 当把子类的指针Upcast的时候,两种Upcast的方式得到的结果分别指向各自的父类镜像
4、 当两个父类重载的虚函数不同时,会使用Thunk机制,也就是说,虚函数表中的函数指针并不指向实际的虚函数,而是指向一小段代码。在这一小段代码中,会修改This指针(ECX寄存器),使之指向合适的父类镜像,然后再跳转到实际的虚函数体。
5、 当不使用虚继承时,共同基类的成员对象,在子类中会有独立两分(从两个父类各自继承了一份)。
6、 当使用虚继承时,共同基类的成员对象也会在虚函数表中记录,访问它必须先查找虚函数表。
另一例子:
#include <iostream>
using namespace std;
class B1
{
public:
int i;
B1(){i=0;}
virtual void func(){i++;}
void func1(){i++;}
virtual void print(){cout<<i<<endl;}
void printB1(){cout<<i<<endl;}
};
class B2
{
public:
int i;
B2(){i=100;}
virtual void func()=0;
virtual void print()=0;
void printB2(){cout<<i<<endl;}
};
class A : public B1,public B2
{
public:
int i;
A(int init)
{
i = init;
}
void func(){i++;}
void print(){cout<<i<<endl;}
};
int main()
{
A a(8);
B1 *base1=(B1*)(&a);
B2 *base2=(B2*)(&a);
base1->func();
base1->printB1();
a.print();
base2->printB2();
a.func1();
base1->print();
base1->printB1();
base2->func();
a.func();
a.print();
base1->printB1();
}
输出:
0
9
100
9
1
11
1
表明当子类和父类在有变量名发生冲突时候,虚函数一定访问的是子类的量,普通函数则根据原所在范围而定(子类的访问子类的,父类的访问父类的)