铸造 - 为什么应该进行向上转型的铸造

5

我看了一些这个网站上的其他帖子,它们提到dynamic_cast和static_cast对于向上转型都是安全的。

为什么需要使用它们进行向上转型呢?

例如,如果类B从A继承,则

A * ptr = new B ();

仍然有效,并且表现为 A 类型的对象。(我也来自 Java 背景,那里不需要进行向上转换的转换。

我还在这个网站上阅读到,dynamic_cast 不需要进行向下转换 [在问题 "When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?" 中]。同样地,我认为只有在进行向下转换时才真正需要转换。

我错在哪里了?


注意:在之前的问题中,有一个答案说“你不必使用它来向下转换...”。然而,从整个句子的上下文来看,意思是你不仅仅局限于只能用它来向下转换。[我已经编辑了那个答案以消除歧义。] - Andy Thomas
3个回答

3
如果您有复杂的继承层次结构,则默认的向上转型行为可能无法正常工作。例如,请考虑以下情况:
struct A {};
struct B : A {};
struct C : A {};
struct D : B, C {};

在这个层次结构中,类型为D的对象具有两个不同的类型为A的基类。从D*A*的转换是含糊的。对于这种类型的情况,您可以强制进行一个中间步骤。
A *p = static_cast<B*>(d);   // Give me the 'A' subobject within 'B'

同样地,如果有一个函数有多个重载版本,既可以接受基类也可以接受派生类,而你需要调用接受基类的版本(注意:这里可能存在代码异味!),那么你可以使用强制类型转换来指定重载解析。同样地,如果你需要指定重载解析,那么你可能正在做其他错误的事情。
除了一些特殊情况(例如默认向上转型不起作用时),实际上没有理由明确使用强制类型转换操作。我实际上建议避免使用它们,因为强制类型转换应该是一个警告信号,过度使用它们可能会使它们变得“正常”,并且在阅读代码时不会引起警惕。

1
好的边角案例示例,但我会质疑它的实际相关性,因为这是糟糕的代码,在你的代码库中不应该出现(死亡之钻),至少在与上转换结合使用时不应该出现。 - Konrad Rudolph

1
在多重继承的情况下(Java实际上没有),您可能需要调整“this”指针。在这种情况下,reinterpret_cast是不安全的,这就是他们试图通过说static_cast和dynamic_cast是安全的来强调的内容。
小提示,在一个要移植到Android而没有dynamic_cast的代码库中进行调查时,我发现了127个dynamic_casts,其中122个实际上并不必要。这些代码是由精通C ++的人编写的。

0
这些为什么需要用于向上转型?
它们并不需要,而且将它们用于向上转型是具有误导性的(在我看来):没有任何理由需要强制类型转换:逻辑上,子类对象是超类实例的一种。
总之,我不鼓励显式向上转型。对于坚持要求使用它的人,我至少要求一致性:无论在初始化中还是其他地方,都要使用显式向上转型。
A * ptr{static_cast<A*>(new B)};

大多数人都会同意这很荒谬。


这正是我所想的。但是,为什么一个得到了673个赞的答案会说“你不必使用它来向下转换”关于动态转换的问题https://dev59.com/IXRC5IYBdhLWcg3wVvct。我没有在那个帖子上发帖,因为我认为我的理解中有一些根本性的错误。 - user929404
1
@user929404 你误解了那个回答——实际上,它的意思是 reinterpret_cast 不仅适用于向下转换,还可以执行其他转换。但是,这篇文章并没有提到必须使用它进行向上转换(然而,其他在线评论确实建议这样做)。 - Konrad Rudolph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接