虚拟的概念

时间:2013-10-05 15:55:17

标签: c++ inheritance virtual

我是CPP的新手,了解晚期结合多态性。

根据我已阅读和理解的内容,虚拟关键字用于后期绑定。 在编译时内部创建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-&GT; D1::F1();它给出了编译错误

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

问题:为什么?不应该将D1 F1打印为虚拟功能。

投掷 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)

问题: 类BASE的虚拟表不会被Class D1,Class D1 vTable&amp; amp;类BASE由DD1继承。?如何进行虚拟表的继承?

5 个答案:

答案 0 :(得分:4)

  

继承自D1的DD1将不具有任何自己的vtable。

不,错了。它将拥有自己的vtable,因为它会覆盖虚函数(此处隐含virtual关键字,因为一旦函数在基类中声明为virtual,它就是虚拟的。)

  

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

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

static_cast<D1*>(b1)->D1::f1();

答案 1 :(得分:2)

我建议你选一本好书(我建议用C ++思考,免费提供),然后浏览一下虚函数这一章,以明确这个令人困惑的话题。

那就是说,你遇到的问题很少。

引用:继承自BASE的D1将继承vtable:

D1::f1()

BASE::f1

实际上,如果选择覆盖基类虚函数,则会在派生类的情况下替换vtable内容。在你的情况下,你已经在D1中完成了。因此D1的vtable将具有D1功能(是的,f1()和f2())。

所以D1的VTable是:

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

基类功能在D1 vTable中消失/被覆盖。

DD1 vtable中包含DD1的功能,而不是D1。

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

答案 2 :(得分:2)

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

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

可以想象正确。也许它应该产生“D1 F1”,语言律师,请帮助。

b1->D1::f1();//will print "D1 F1"

为什么要这样做? b1的类型为BASE *,如果您想声明需要显式转换,编译器没有理由相信该对象实际上是D1

b1->BASE::f1();//will print"BASE F1"

答案 3 :(得分:1)

  

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

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

dynamic_cast<D1*> (b1) -> f1()

答案 4 :(得分:0)

你不能从Base类做完全限定名(FQN)方法调用,它没有关于D1类的线索。

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

(dynamic_cast<DD1*>(b1))->D1::f1();//will print "D1 F1"