4

我对 CPP 很陌生,正在学习后期绑定多态性。

根据我的阅读和理解,virtual 关键字用于后期绑定。它在编译时在内部创建一个由 vptr 指向的 vtable。所以,例如

class BASE{
public:
virtual void f1(){cout<<"BASE F1\n";}
virtual void f2(){cout<<"BASE F2\n";}
void f3(){cout <<"BASE F3\n";}
};

class D1:public BASE{
public: 
    virtual void f1(){cout<<"D1 F1\n";}
    void f2(){cout<<"D1 F2\n";}  
};

class DD1:public D1{
public:
    void f1(){cout<<"DD1 F1\n";}
    void f2(){cout <<"DD1 F2\n";}
};

在这里,BASE 将在基类 vtable 中有 2 个函数:

BASE::f1() 
BASE::f1()

从 BASE 继承的 D1 将继承 vtable:

D1::f1()
BASE::f1

从 D1 继承的 DD1 将没有任何自己的 vtable。

当我们创建一个对象时:

//case 1:
BASE *b = new D1(); 
b->f1();//will print "D1 F1"
b->BASE::f1();//will print "BASE F1"
b->f2();//will print "D1 F2"
//case 2:
BASE *b1 = new DD1();
b1->f1();//will print "DD1 F1"
b1->D1::f1();//will print "D1 F1"
b1->BASE::f1();//will print"BASE F1"

但是,在这种情况下: b1->D1::f1(); 它给出了一个编译错误

 error: ‘D1’ is not a base of ‘BASE’

问:为什么?它不应该打印 D1 F1 作为它的虚函数。

在 throw fdump之后,我发现了一件更有趣的事情,这有点令人困惑;

Vtable for BASE
BASE::_ZTV4BASE: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI4BASE)
16    (int (*)(...))BASE::f1
24    (int (*)(...))BASE::f2

Class BASE
   size=8 align=8
   base size=8 base align=8
BASE (0x7fbc3d2ff120) 0 nearly-empty
    vptr=((& BASE::_ZTV4BASE) + 16u)

Vtable for D1
D1::_ZTV2D1: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI2D1)
16    (int (*)(...))D1::f1
24    (int (*)(...))D1::f2

Class D1
   size=8 align=8
   base size=8 base align=8
D1 (0x7fbc3d31f2d8) 0 nearly-empty
    vptr=((& D1::_ZTV2D1) + 16u)
  BASE (0x7fbc3d2ff180) 0 nearly-empty
      primary-for D1 (0x7fbc3d31f2d8)

Vtable for DD1
DD1::_ZTV3DD1: 4u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI3DD1)
16    (int (*)(...))DD1::f1
24    (int (*)(...))DD1::f2

Class DD1
   size=8 align=8
   base size=8 base align=8
DD1 (0x7fbc3d31f3a8) 0 nearly-empty
    vptr=((& DD1::_ZTV3DD1) + 16u)
  D1 (0x7fbc3d31f410) 0 nearly-empty
      primary-for DD1 (0x7fbc3d31f3a8)
    BASE (0x7fbc3d2ff1e0) 0 nearly-empty
        primary-for D1 (0x7fbc3d31f410)

问:Class BASE的Virtual table会不会被Class D1继承,Class D1 vTable & Class BASE会不会被DD1继承?虚拟表的继承将如何发生?

4

5 回答 5

4

从 D1 继承的 DD1 将没有任何自己的 vtable。

没有错。它将有自己的 vtable,因为它覆盖了虚函数(virtual这里隐含了关键字,因为一旦virtual在基类中声明了一个函数,它在任何地方都是虚函数)。

问:为什么?它不应该打印 D1 F1 作为它的虚函数。

静态类型是,即使它的动态类型b1是。因此,您不能调用,因为该语法指示编译器静态地解析对(再次,静态地)在. 如果你绝对想执行这个调用,并且知道它的动态类型实际上是(或从它派生的),你可以强制转换它来改变对象的静态类型:Base*DD1*b1->D1::f1b1b1D1

static_cast<D1*>(b1)->D1::f1();
于 2013-10-05T16:15:04.840 回答
2

至少您的一些期望是错误的:

//案例2:
基础 *b1 = 新 DD1();
b1->f1();//将打印“DD1 F1”

可想而知是正确的。或许它应该产生“D1 F1”,语言律师,请帮助。

b1->D1::f1();//将打印“D1 F1”

为什么要这样做? b1是 type BASE *,编译器没有理由相信对象实际上是 a D1,如果你想断言你需要一个显式转换。

b1->BASE::f1();//将打印“BASE F1”

是的。

于 2013-10-05T16:16:20.790 回答
2

我会建议你选择一些好书(我建议用 C++ 思考,免费提供)并阅读关于虚拟函数的章节,以清楚地了解这个令人困惑的话题。

也就是说,你做错的事情很少。

Quote: D1 从 BASE 继承,将继承 vtable:

D1::f1()

BASE::f1

实际上发生的情况是,如果派生类选择覆盖基类虚函数,则会替换 vtable 内容。在你的情况下,你已经在 D1 中做到了。所以 D1 的 vtable 将具有 D1 功能(是的,它们都是 f1() 和 f2() )。

所以 D1 的 VTable 是:

D1::f1()
D1::f2()

基类函数在 D1 vTable 中消失/被覆盖。

DD1 vtable里面有DD1的功能,而不是D1。

关于您看到的错误,答案已经发布。

于 2013-10-05T16:14:27.290 回答
1

“为什么不打印 D1 F1 作为它的虚函数?”

因为指针b1的类型是BASE. 您实际上是在尝试从编译器不允许的 BASE 对象访问 D1 类。您需要做的是通知编译器该b1指针是一个有效的 D1 对象,如下所示:

dynamic_cast<D1*> (b1) -> f1()
于 2013-10-05T16:09:43.310 回答
0

您不能从 Base 类调用完全限定名称 (FQN) 方法,它不知道类 D1。

这是解决方案 - 向下转换为 DD1 或 D1,然后进行 FQN 调用:

(dynamic_cast<DD1*>(b1))->D1::f1();//will print "D1 F1"
于 2013-10-05T16:21:42.227 回答