Ste*_*enR 6 c++ virtual inheritance pointers casting
我想知道当我们使用向下转换和向上转换时指针转换中究竟发生了什么.我有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;
}
Run Code Online (Sandbox Code Playgroud)
问题3:我正在尝试总结一条规则,以推断如果我们使用虚拟函数和指针一起投射会发生什么,但我无法弄明白.
最初,我认为虚函数将引导我们到指针真正指出的位置.因此,当我们打字
A* paUpcast= new B();
paUpcast->f();
Run Code Online (Sandbox Code Playgroud)
如果Af()是虚函数,则第二行将显示"B",因为paUpcast实际上指向B对象
但是,当我们打字
B* pbDowncast=(B*)pa;
pbDowncast->f();
Run Code Online (Sandbox Code Playgroud)
并且它将显示"A"而不是"B",这使矛盾发生.
谁能解释或给我一些提示?非常感谢
我会尝试解释我是如何理解它的.帮助我的小贴士是考虑乐高积木.
在你的情况下,我们有两个乐高积木,一个命名A,另一个命名B...但只是想象一下这B件作品是通过连接两件而形成的一件,其中一件是同一类型的A:
A B
+-+ +-+-+
|a| |a|b|
+-+ +-+-+
Run Code Online (Sandbox Code Playgroud)
然后,你使用指针来评估每个乐高积木,但每件都有自己的形状,所以,想象一下:
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|
+-+-+
Run Code Online (Sandbox Code Playgroud)
请注意,paUpcast指针是一个类型的指针,A但是拿着一块类型B,该B块与A一个不同,正如您所看到的那样是一块略大于它的底座.
这是你正在谈论的upcasting,基指针就像一个通配符,可以在继承树上保存任何相关的东西.
A*paUpcast = new B();
上面的线是否等于以下?
A*paUpcast =(A*)B;
好吧,假设你真的想写这个:A* paUpcast = (A*) new B();是的,它是.您创建B该类的新实例并将其存储到指向A类的指针中,在分配到指针之前转换新实例不会改变它无论如何都将存储到基类指针中的事实.
为什么我们不能使用以下2个代码;
B*pbDowncast = new A();
B*pbDowncast =(B*)A;
记住乐高积木.做什么会发生什么B* pbDowncast=new A()?:
B* pbDowncast --> +-+ new A()
|a|
+-+
Run Code Online (Sandbox Code Playgroud)
创建一个新的base clas实例并将其存储到指向派生类的指针中,如果仔细观察lego文件是否合适,你会尝试将基础视为派生.这A件作品缺少必要的额外东西B ; 所有这些东西"存储"到乐高积木的额外部分,B = all the A stuff plus something more:
B
+-+-----------------------------------+
|a|extra stuff that only B pieces have|
+-+-----------------------------------+
Run Code Online (Sandbox Code Playgroud)
如果你试图调用只有B该类的方法会发生什么?有一个B指针,你可以调用所有的B方法,但是你创建的实例来自A没有B方法的实体,它不是用所有这些额外的东西创建的.
但是,当我们打字
B*pbDowncast =(B*)pa;
pbDowncast-> F();
显示"A"而不是"B",这使矛盾发生.
它没有解释我的矛盾,记住乐高积木,pa指针指向一块类型A:
A *pa --> +-+
|a|
+-+
Run Code Online (Sandbox Code Playgroud)
这件事缺乏所有的B东西,事实是缺乏在标准输出上f()打印的方法B......但它有一个在输出f()上打印的方法A.
我希望它有所帮助!
编辑:
看来你也同意使用垂头丧气是不合适的吗?
不,我不同意.转发根本不是不合适的,但取决于它的用途它将是不合适的.与所有C++工具一样,向下转换具有实用性和使用范围; 所有尊重良好用途的诡计都是合适的.
那么什么是良好的使用向下转换工具呢?恕我直言,任何不会破坏代码或程序流程的东西,如果程序员知道他在做什么,维护代码尽可能可读(对我来说最重要).
倾向于采用可能的继承分支是一种常见的做法:
A* paUpcast = new B();
static_cast<B*>(paUpcast)->f();
Run Code Online (Sandbox Code Playgroud)
但是对于更复杂的继承树来说会很麻烦:
#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!
Run Code Online (Sandbox Code Playgroud)
要解决这个问题,你可以使用 dynamic_cast
A* paUpcast = new C();
if (B* b = dynamic_cast<B*>(paUpcast))
{
b->f();
}
if (C* c = dynamic_cast<C*>(paUpcast))
{
c->f();
}
Run Code Online (Sandbox Code Playgroud)
但dynamic_cast众所周知,它缺乏性能,你可以研究一些替代方法dynamic_cast,比如内部对象标识符或转换运算符,但为了坚持这个问题,如果正确使用,那么向下转换就不错了.