C++ 虚拟现实的概念
我对CPP非常陌生,学习了后期绑定多态性 根据我的阅读和理解,virtual关键字用于后期绑定。 它在编译时在内部创建一个由vptr指向的vtable。 所以 比如说C++ 虚拟现实的概念,c++,inheritance,virtual,C++,Inheritance,Virtual,我对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 B
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继承的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打印为其虚拟功能。
在进行了掷骰后,我发现了一件有趣的事情,这有点令人困惑
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)
问题:
基类的虚拟表不会被类D1继承,类D1 vTable&基类不会被DD1继承。?如何继承虚拟表?
“为什么不将D1 F1打印为其虚拟功能?”
因为指针b1
的类型为BASE
。您实际上是在尝试从基本对象访问D1类,而编译器不允许这样做。您需要做的是通知编译器b1
指针是有效的D1对象,如下所示:
dynamic_cast<D1*> (b1) -> f1()
dynamic_cast(b1)->f1()
< P>我建议你选一些好的书(我建议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
关于你看到的错误,答案已经公布了
从D1继承的DD1将没有自己的任何vtable
不,错了。它将有自己的vtable,因为它覆盖了虚拟函数(这里暗示了virtual
关键字,因为一旦在基类中声明了函数virtual
,它就到处都是虚拟的)
问题:为什么?不应将D1 F1打印为其虚拟功能
b1
的静态类型是Base*
,即使其动态类型是DD1*
。因此,您不能调用b1->D1::f1
,因为该语法指示编译器静态解析对函数的调用,而该函数在b1
中不可用。如果您确实希望执行此调用,并且知道b1
的动态类型实际上是D1
(或从中派生),则可以强制转换它以更改对象的静态类型:
static_cast<D1*>(b1)->D1::f1();
static_cast(b1)->D1::f1();
至少您的一些期望是错误的:
//case 2:
BASE *b1 = new DD1();
b1->f1();//will print "DD1 F1"
是。无法从基类执行完全限定名(FQN)方法调用,基类没有关于类D1的线索
以下是解决方案-向下转换到DD1或D1,然后执行FQN调用:
(dynamic_cast<DD1*>(b1))->D1::f1();//will print "D1 F1"
(动态_cast(b1))->D1::f1()//将打印“D1 F1”
不需要动态\u cast
,除非您确实检查了该cast是否成功。这一点很好。Quote“static_cast不执行运行时检查。如果您知道引用了特定类型的对象,则应使用此选项,因此无需进行检查”不要使用static_cast进行向下转换,即使在本例中它很小,也会在实际应用程序中给您带来麻烦。此示例将调用DD1,而不是D1,请参阅我的正确调用(FQN)答案。@IuriCovalisin您显然根本误解了动态强制转换
和静态强制转换
的工作原理。请参阅上面评论中链接的解释。此处为语言律师:它必须生成“DD1 F1”
DD1::f1
是虚拟的
。那么DD1::f1
是虚拟的,即使它没有被宣布为虚拟的?如果我随后重写创建了另一个派生类D31
,并试图通过指向DD1
的指针调用该方法,将调用哪个方法?一旦是虚拟的,就始终是虚拟的。它在基类中声明为虚拟。重写函数不会重新声明它,只会替换它的实现。很遗憾,C++允许在重写函数中省略<代码>虚拟<代码>代码,但效果与写的一样。我认为这是不幸的。我认为“一旦虚拟,就永远是虚拟的”是一件重要而不明显的事情。这就是为什么我在下面(或上面)编辑了你的评论,使之明确。也许你应该用自己的话来编辑它。如果一个成员函数在基类中声明为虚函数,那么它总是虚函数,并且在派生类中关键字是可选的。因此,您在D1
中的f2
与f1
一样被覆盖,就像DD1
中的两个函数一样。对于齿轮传动的回答,相同的注释是:只有在您实际检查强制转换是否成功(以及是否有可能不成功)时,动态强制转换才有意义。否则使用static\u cast
。static cast也无法识别问题-此示例仍然编译并运行BASE*b1=new D1();b1->f1()//将打印“DD1 F1”(静态_cast(b1))->D1::F1()//将打印“D1 F1”我不理解该评论static\u cast
与这里的dynamic\u cast
完全相同,只是后者意味着不同的用法(其含义是“我不确定此强制转换是否成功,因此我需要检查其返回值)。只有在确定类型时才使用static\u cast,并且永远无法确定类型
b1->D1::f1();//will print "D1 F1"
b1->BASE::f1();//will print"BASE F1"
(dynamic_cast<DD1*>(b1))->D1::f1();//will print "D1 F1"