最近在看《深入探索C++对象模型》中的多重继承和虚拟继承,慢慢的就绕进去了,挣扎着挣扎着又出来了,下面将一点心得体会写下来,也算做一个总结。
(1)多重继承
多重继承是指一个类继承自多个基类,在这种情况下,从第二基类开始,基类的首址就不再和派生类的首址一致了,此时通过第二基类及以后的基类调用派生类的虚函数时,就需要调整this指针。为了形象的说明其原因,不妨定义如下继承体系进行说明:
class Base { public: Base() { } virtual void fun() { } }; class Head { public: Head() { } virtual void funccc() { } }; class Head1 : public Head, public Base { public: Head1() { } virtual void func() { } virtual ~Head1(){} };
其中,Head1类继承自Base类和Head类,这两个基类都包含有虚函数。这里需要明确一点,对于继承自n个非虚基类的派生类,包含有n个vptr,分别指向对应的基类的虚表,而派生类新增的虚函数通常写在第一张基表中。
这里在VS2010环境下,通过CL内存分布查看工具(http://www.cnblogs.com/dsky/archive/2012/02/07/2340984.html)来查看派生类Head1的内存分布:
可以看到Head1中包含两个virtual table,第一个virtual table是主table,其中包含派生类新增的虚函数地址,若加上一层继承,即添加如下派生类:
class Head2 : public Head1 { public: Head2() { } virtual void fund(){} };
那么该派生类新增的虚函数也会增加到主virtual table中,得到的内存布局如下:
(2)含有虚基类的继承
引入虚基类的主要原因是为了应对继承体系中出现的基类被多次继承的情况,在这种情况下引入虚基类是为了使派生类中只有基类的一个实体。为此,在派生类中插入一个指针来定位到该虚基类的实体,如果有多个虚基类,那么为派生类创建一张虚基类表,并未每一个虚基类添加一个指向该虚基类的指针,这样,不管有多少个虚基类,都只有一个指向虚基类表的指针用来定位这些虚基类的实体。
但与不包含虚基类的多重继承不同的是,虚基类若含有虚指针,该虚指针不会被派生类继承,派生类不能通过修改虚基类的指针来指向自己的虚表。简单地讲,虚基类的虚指针与派生类是否包含虚指针没有关系,彼此不影响,这完全不同于单一继承(派生类中只有一个虚指针)和普通的多重继承(第一个包含有虚指针的基类的虚指针被派生类所有)。
以下面为例:
1)派生类中不包含虚函数:
class Base { public: Base() { } virtual void fun() { } }; class Head { public: Head() { } virtual void funccc() { } }; class Head1 : public virtual Head, public virtual Base { public: Head1() { } };
对应的内存布局:
如果修改派生类Head1如下:
class Head1 : public virtual Head, public virtual Base { public: Head1() { } virtual void func() { } virtual ~Head1(){} };
对应的内存布局如下:
可以看到Head1多了一个vfptr,即多了一个虚指针,指向的虚表里只包含自己新增的虚函数地址,而不包含基类的虚函数。