虚函数

虚函数的结束结点,标志了虚函数表的结束。

WinXP+VS2003下,这个值是NULL

而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,这个值是如果1表示还有下一个虚函数表,如果值是0,表示是最后一个虚函数表。

一般继承(无虚函数覆盖)

1)虚函数按照其声明顺序放于表中。

2)父类的虚函数在子类的虚函数前面。

一般继承(有虚函数覆盖)

1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。

2)没有被覆盖的函数依旧。

复杂一些的情况:

 

多重继承(无虚函数覆盖)

 

 我们可以看到:

1)  每个父类都有自己的虚表。
2)  子类的成员函数被放到了第一个父类的表中。
3)  内存布局中,其父类布局依次按声明顺序排列。
4)  每个父类的虚表中的f()函数都被overwrite成了子类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

重复继承

我们可以看见,最顶端的父类B其成员变量存在于B1和B2中,并被D给继承下去了。而在D中,其有B1和B2的实例,于是B的成员在D的实例中存在两份,一份是B1继承而来的,另一份是B2继承而来的。所以,如果我们使用以下语句,则会产生二义性编译错误:

D d;

d.ib = 0;               //二义性错误

d.B1::ib = 1;           //正确

d.B2::ib = 2;           //正确

注意,上面例程中的最后两条语句存取的是两个变量。虽然我们消除了二义性的编译错误,但B类在D中还是有两个实例,这种继承造成了数据的重复,我们叫这种继承为重复继承。重复的基类数据成员可能并不是我们想要的。所以,C++引入了虚基类的概念。
 

钻石型多重虚拟继承

上述的“重复继承”只需要把B1和B2继承B的语法中加上virtual 关键,就成了虚拟继承

把B这个超类放到了最后

一、通过父类型的指针访问子类自己的虚函数

我们知道,子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数,但我们根本不可能使用下面的语句来调用子类的自有虚函数:

          Base1 *b1 = new Derive();

            b1->f1();  //编译出错

任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法。

二、访问non-public的虚函数

另外,如果父类的虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,所以,我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数,这是很容易做到的。

我们可以通过得到虚函数指针和偏移的方式取得上述说的两种情况。

转载:https://blog.csdn.net/haoel/article/details/3081385

原文地址:https://www.cnblogs.com/Lune-Qiu/p/9357299.html