我会尝试解释我所理解的内容。有一个帮助我的提示是将其类比成乐高积木。
在你的情况下,我们有两个乐高积木,一个名为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
类的指针中,在赋值之前转换新实例并不会改变它将被存储到基类指针中的事实。
为什么我们不能使用以下两个代码:
B* pbDowncast=new A();
B* pbDowncast = (B*) A;
记住乐高积木。在执行
B* pbDowncast=new A()
时会发生什么呢?
B* pbDowncast --> +-+ new A()
|a|
+-+
创建一个新的基类实例并将其存储到指向派生类的指针中,您试图将基类视为派生类。如果仔细观察,乐高积木块不适合!
A
块缺少被认为是
B
种类所必需的
额外部分;所有这些东西“都存储”在乐高积木块的额外部分中,
B = 所有A的内容加上更多的东西
:
B
+-+-----------------------------------+
|a|extra stuff that only B pieces have|
+-+-----------------------------------+
如果你尝试调用只有类
拥有的方法会发生什么情况?有一个
指针,可以调用所有
方法,但是你创建的实例来自
种类,不会有
方法,它没有被创建为这个额外的东西。
然而,当我们键入
B * pbDowncast =(B *)pa;
pbDowncast->f();
显示“A”而不是“B”,这导致矛盾发生。
对我来说,这并不像矛盾,记住乐高积木,
pa
指针指向的是类型为
的积木片。
A *pa --> +-+
|a|
+-+
这段文字缺少所有的B内容,事实上它缺少了在标准输出上打印B的f()方法...但它有一个在输出上打印A的f()方法。希望对您有所帮助!
编辑:
“看起来你也认为使用向下转型是不合适的,不是吗?”
不,我不同意。向下转型并不完全不合适,但根据其用途可能是不合适的。像所有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();
为了解决这个问题,你可以使用
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
因其性能不佳而广为人知,你可以学习一些替代方案,例如内部对象标识符或转换运算符,但为了回答问题,如果正确使用,向下转型并不是坏事。