C++ 虚函数

 2024-02-25 04:04:39  阅读 0

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

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

Base1 *b1 = new();

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

任何试图使用父类指针调用子类中未重写父类的成员函数的行为都会被编译器认为是非法的,因此这样的程序根本无法编译。 但在运行时,我们可以通过指针访问虚函数表,从而实现违反C++语义的行为。 (为了尝试这个,通过阅读附录中的代码,我相信你可以做到这一点)

2. 访问非虚函数

另外,如果父类的虚函数是或者是,这些非虚函数也会存在于虚函数表中,所以我们也可以通过访问虚函数表来访问这些非虚函数。 这很容易做到。

喜欢:

基类{

:

无效 f() { 输出

通过这个例子我们可以看到,我们可以通过将&b强行转换为int *来获取虚函数表的地址,然后再次取地址就可以获取到第一个虚函数的地址,即Base::f() ,在上面的程序中得到了验证(int*被强制转为函数指针)。 通过这个例子我们可以知道,如果我们要调用Base::g()和Base::h(),代码如下:

(有趣)*((int*)*(int*)(&b)+0); // 基址::f()

(有趣)*((int*)*(int*)(&b)+1); // 基址::g()

(有趣)*((int*)*(int*)(&b)+2); // 基类::h()

这个时候你应该明白了。 什么? 还是有点头晕。 另外,这段代码看起来太乱了。 没问题,我画个图来解释一下。 如下:

注意:上图中,我在虚函数表的末尾添加了一个额外的节点。 这是虚函数表的结束节点,就像字符串的终止符“\0”一样,它标记了虚函数表。 结尾。 这个结束标志在不同的编译器下的值是不同的。 在WinXP+下,该值为NULL。 在7.10 + Linux 2.6.22 + GCC 4.1.3下,如果该值为1,则表示存在下一个虚拟函数表。 如果该值为0,则表示它是最后一个虚函数表。

下面,我将解释一下“无覆盖”和“有覆盖”时的虚函数表是什么样子的。 不重写父类的虚函数是没有意义的。 我之所以想谈论没有报道的情况,主要是为了提供一个比较。 通过对比,我们可以更清楚地知道其内部的具体实现。

一般继承(无虚函数重写)

接下来我们看看继承时的虚函数表是什么样子的。 假设存在如下所示的继承关系:

请注意,在这种继承关系中,子类不会重载父类的任何函数。 然后,在派生类的实例中,其虚函数表如下所示:

例如:d的虚函数表; 如下:

我们可以看到以下几点:

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

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

相信如果你聪明的话,可以参考前面的程序,写一个程序来验证一下。

通用继承(具有虚函数覆盖)

重写父类的虚函数是显而易见的,否则,虚函数就变得毫无意义。 接下来我们看看,如果子类中有一个虚函数重载了父类的虚函数,会是什么样子呢? 假设我们有如下的继承关系。

为了让大家看到继承的效果,在这个类的设计中,我只覆盖了父类的一个函数:f()。 那么,对于派生类的实例,其虚函数表将如下所示:

从表中我们可以看出以下几点:

虚函数表typeinfo_c虚函数表_c语言虚函数表

1)重写的f()函数被放置在虚表中原来父类虚函数的位置。

2) 未被覆盖的功能保持不变。

这样,我们可以看到对于像下面这样的程序,

基数*b = new();

b->f();

b指向的内存中虚函数表中f()的位置已经被::f()函数地址替代,所以当实际调用发生时,调用::f()。 这样就实现了多态性。

多重继承(无虚函数重写)

接下来我们看看多重继承中的情况。 假设存在如下类的继承关系。 注意:子类不会覆盖父类的函数。

对于子类实例中的虚函数表,它看起来像这样:

我们可以看到:

1)每个父类都有自己的虚表。

2)子类的成员函数放在第一个父类的表中。 (所谓第一个父类是按照声明的顺序来判断的)

这样做是为了解决不同父类类型的指针指向同一个子类实例的问题,从而可以调用实际的函数。

多重继承(具有虚函数覆盖)

我们来看看如果发生虚函数覆盖会发生什么情况。

下图中,我们在子类中重写了父类的f()函数。

下面是子类实例中的虚函数表的图:

我们可以看到三个父类虚函数表中f()的位置已经被子类的函数指针取代了。 这样,我们就可以将任何静态类型的父类指向子类,并调用子类的f()。 喜欢:

d;

基数1 *b1 = &d;

基数2 *b2 = &d;

Base3 *b3 = &d;

b1->f(); //::F()

b2->f(); //::F()

b3->f(); //::F()

b1->g(); //Base1::g()

b2->g(); //Base2::g()

b3->g(); //Base3::g()

安全

每次我写一篇关于C++的文章时,我总是要批评C++。 本文也不例外。 通过上面的描述,相信大家对虚函数表有了更加详细的了解。 水能载舟,也能覆舟。 接下来,让我们看一下虚函数表可以做的一些坏事。

标签: 虚函数 多态

如本站内容信息有侵犯到您的权益请联系我们删除,谢谢!!


Copyright © 2020 All Rights Reserved 京ICP5741267-1号 统计代码