我有2个问题.其中前两个是评论. Q3结束了.
#include<iostream> using namespace std; class A{ public: virtual void f() { cout<<"A"<<endl; } }; class B: public A{ public: virtual void f() { cout<<"B"<<endl; } }; int main() { A* pa =new A(); B* pb =new B(); A* paUpcast= new B(); /* Q1: Is the line above equivalent to the following? A* paUpcast = (A*) B; */ B* pbDowncast=(B*)paUpcast; /* Q2:Why we cannot use the following 2 code; B* pbDowncast=new A(); B* pbDowncast = (B*) pa; */ pa->f(); pb->f(); paUpcast->f(); pbDowncast->f(); return 1; }
问题3:我正在尝试总结一条规则,以推断如果我们使用虚拟函数和指针一起投射会发生什么,但我无法弄明白.
最初,我认为虚函数将引导我们到指针真正指出的位置.
因此,当我们打字
A* paUpcast= new B(); paUpcast->f();
如果A.f()是虚函数,则第二行将显示“B”,因为paUpcast实际上指向B对象
但是,当我们打字
B* pbDowncast=(B*)pa; pbDowncast->f();
并且它将显示“A”而不是“B”,这使矛盾发生.
谁能解释或给我一些提示?非常感谢
解决方法
在你的情况下,我们有两个lego片,一个名为A,另一个名为B ……但是想象一下B片是通过连接两个片而形成的片,其中一个是相同类型的A:
A B +-+ +-+-+ |a| |a|b| +-+ +-+-+
然后,你使用指针来评估每个乐高积木,但每件都有自己的形状,所以,想象一下:
A* pa =new A(); B* pb =new B(); A* paUpcast= new B(); A *pa --> +-+ new A() |a| +-+ B* pb --> +-+-+ new B() |a|b| +-+-+ A* paUpcast --> +-+-+ new B() |a|b| +-+-+
请注意,paUpcast指针是A类型的指针,但是拿着一块B型,B片与A片不同,正如您所看到的那样,它是一块略大于其底座的片.
这是你正在谈论的upcasting,基指针就像一个通配符,可以在继承树上保存任何相关的东西.
A* paUpcast= new B();
Is the line above equivalent to the following?
A* paUpcast = (A*) B;
好吧,假设你真的想写这个:A * paUpcast =(A *)new B();是的.您创建B类的新实例并将其存储到指向A类的指针中,在分配到指针之前转换新实例不会改变它无论如何都将存储到基类指针中的事实.
Why we cannot use the following 2 code;
B* pbDowncast=new A();
B* pbDowncast = (B*) A;
记住乐高积木.做B * pbDowncast = new A()时会发生什么?:
B* pbDowncast --> +-+ new A() |a| +-+
创建一个新的base clas实例并将其存储到指向派生类的指针中,如果仔细观察lego文件是否合适,你会尝试将基础视为派生. A片缺少必须考虑B类的额外东西;所有这些东西“存储”到乐高积木的额外部分,B =所有的东西加上更多东西:
B +-+-----------------------------------+ |a|extra stuff that only B pieces have| +-+-----------------------------------+
如果你试图调用只有B类的方法会发生什么?有一个B指针你可以调用所有的B方法,但是你创建的实例来自A类,它没有B方法,它不是用所有这些额外的东西创建的.
However,when we type
B* pbDowncast=(B*)pa;
pbDowncast->f();
display “A” instead of “B”,which make the contradiction happen.
它没有解释我的矛盾,记住lego片段,pa指针指向A型片:
A *pa --> +-+ |a| +-+
这篇文章缺少所有B的东西,事实是缺少在标准输出上打印B的f()方法……但它有一个方法f()在输出上打印A.
我希望它有所帮助!
编辑:
It seems that you also agree that it is inappropriate to use the downcast isn’t it?
不,我不同意.转发根本不是不合适的,但取决于它的用途它将是不合适的.与所有C工具一样,向下转换具有实用性和使用范围;所有尊重良好用途的诡计都是合适的.
那么什么是良好的使用向下转换工具呢?恕我直言,任何不会破坏代码或程序流程的东西,如果程序员知道他在做什么,维护代码尽可能可读(对我来说最重要).
倾向于采用可能的继承分支是一种常见的做法:
A* paUpcast = new B(); static_cast<B*>(paUpcast)->f();
但是对于更复杂的继承树来说会很麻烦:
#include<iostream> using namespace std; class A{ public: virtual void f() { cout<<"A"<<endl; } }; class B: public A{ public: virtual void f() { cout<<"B"<<endl; } }; class C: public A{ public: virtual void f() { cout<<"C"<<endl; } }; A* paUpcast = new C(); static_cast<B*>(paUpcast)->f(); // <--- OMG! C isn't B!
要解决这个问题,您可以使用dynamic_cast
A* paUpcast = new C(); if (B* b = dynamic_cast<B*>(paUpcast)) { b->f(); } if (C* c = dynamic_cast<C*>(paUpcast)) { c->f(); }
但是dynamic_cast是众所周知的lack of performance,你可以研究dynamic_cast的一些替代方案,比如内部对象标识符或转换运算符,但为了坚持这个问题,如果正确使用,那么向下转换也不错.