我会尝试解释我是如何理解它的。帮助我的提示是考虑乐高积木。
在您的情况下,我们有两个乐高积木,一个名为 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一块,你可以看到是一块比它的基数略大。
这就是你所说的向上转换,基指针就像一个通配符,可以在继承树上向下保存任何相关内容。
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|
+-+
创建一个新的基类实例并将其存储到派生类的指针中,如果您仔细观察乐高积木不合适,您会尝试将基类视为派生类! 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 的东西,事实是缺少在标准输出上打印B 的f() 方法......但它有一个打印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 是众所周知的lack of performance,您可以研究dynamic_cast 的一些替代方案,例如内部对象标识符或转换运算符,但为了坚持这个问题,向下转换一点也不差如果使用得当。