我会尝试解释我是如何理解它的。帮助我的提示是考虑乐高积木。
在你的情况下,我们有两个乐高积木,一个命名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
指针是 type 的指针,A
但持有一块 type B
,这块与B
那块不同A
,正如您所看到的,它比它的基数略大。
这就是你所说的向上转换,基指针就像一个通配符,可以在继承树上向下保存任何相关的东西。
A* paUpcast= 新 B();
上面的行是否等同于以下行?
A* paUpcast = (A*) B;
好吧,假设你真的想写这个:A* paUpcast = (A*) new B();
是的,它是。您创建B
类的新实例并将其存储到指向A
类的指针中,在分配到指针之前转换新实例不会改变它将存储到基类指针中的事实。
为什么我们不能使用以下2个代码;
B* pbDowncast=新 A();
B* pbDowncast = (B*) A;
记住乐高积木。做的时候会发生什么B* pbDowncast=new A()
?:
B* pbDowncast --> +-+ new A()
|a|
+-+
创建一个新的基类实例并将其存储到派生类的指针中,如果您仔细观察乐高积木不合适,您会尝试将基类视为派生类!该A
作品缺少需要考虑的额外内容B
;所有这些东西都“存储”到乐高积木的额外部分中,B = all the A stuff plus something more
:
B
+-+-----------------------------------+
|a|extra stuff that only B pieces have|
+-+-----------------------------------+
如果您尝试调用只有B
类具有的方法会发生什么?拥有一个B
指针,您可以调用所有B
方法,但是您创建的实例来自A
没有B
方法的类型,它不是用所有这些额外的东西创建的。
但是,当我们键入
B* pbDowncast=(B*)pa;
pbDowncast->f();
显示“A”而不是“B”,这使矛盾发生。
这对我来说并不矛盾,记住乐高积木,pa
指针指向一块类型A
:
A *pa --> +-+
|a|
+-+
这篇文章缺少所有的B
东西,事实是缺少在标准输出上f()
打印的方法B
……但它有一个在输出上f()
打印的方法A
。
我希望它有帮助!
编辑:
看来您也同意使用downcast是不合适的不是吗?
不,我不同意。向下转换根本不是不合适的,但根据它的用途它会是不合适的。与所有 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
众所周知,它缺乏性能,您可以研究一些替代方法dynamic_cast
,例如内部对象标识符或转换运算符,但为了解决这个问题,如果正确使用向下转换,它一点也不坏。