Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/132.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 虚拟现实的概念_C++_Inheritance_Virtual - Fatal编程技术网

C++ 虚拟现实的概念

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

我对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继承的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"