【问题标题】:In C++, why does casting to reference of derived type work?在 C++ 中,为什么强制转换为派生类型的引用?
【发布时间】:2016-03-23 13:25:39
【问题描述】:

确切地说,为什么B b = (B&) a 可以编译和工作,而B b = (B) a 在下面的程序中却没有?

#include <iostream>
using namespace std;

class A {public: void f(){ cout<<"A"<<endl;} };

class B : public A { public: void f(){cout<<"B"<<endl;} };

void g(A a){  B b = (B&) a; b.f(); }

int main() {
    B b; g(b);
    return 0;
}

有没有关于我在这里缺少的引用转换为派生类型的东西?如果我只是转换为 B,它会给出构造函数 B(A a) 不存在的编译时错误。

【问题讨论】:

  • “为什么”是什么意思?您是否需要指定此行为的标准引用?或者是一个深刻的哲学原因,为什么允许这样做是个坏主意?
  • 显然是第一个。我不知道为什么标准允许转换为派生类型引用而不是派生类型实例。如果引用按标准解释为指针,那将是有意义的。
  • 当你只需要'\n'时不要使用std::endl

标签: c++ inheritance casting reference derived-instances


【解决方案1】:

因为从AB 的隐式转换不存在,而且您也没有明确定义。

另一方面,引用转换是有效的,因为它允许用于继承的类型。更准确地说,您可以在同一继承层次结构中的不同类之间进行两种转换。指针也是如此。相关概念称为多态性,如果您想进一步研究一些指针。

但请注意,只有将 类型为 B 的对象转换为 B 才有意义。例如:

B b;
A& aRef = B; // equivalent of A& ref = (A&)B;
B& bRef = (B&)aRef;

一旦您尝试访问B 中不存在的A 中的某些数据或方法,您所做的操作将在运行时失败。因为你的实际对象是A,而不是B

向上转换(从后代到上层)始终是安全的,因为继承基类的类的任何对象都是有效的基础对象。然而,由于我上面解释的确切原因,向下转换是危险的,并且永远不应该使用 C 风格的转换来完成。相反,请使用dynamic_cast:

B b;
A& aRef = B;
B& bRef = dynamic_cast<B&>(aRef);

dynamic_cast 使用 RTTI(运行时类型信息)来验证操作,如果转换无效,将抛出 std::bad_cast 异常。这与dynamic_casting 指针不同,在这种情况下,转换返回nullptr 而不是抛出异常。

【讨论】:

  • 我在函数调用中确保只有派生类型的实例被传递给以基类型为参数的函数。因此永远不会发生运行时故障。另外,您是否想说将类型转换为派生类型的引用是因为在幕后,C++ 将引用解释为指针?因为我知道在此处使用指针代替引用是可行的。我只是不确定这是一个将引用解释为指针的 C++ 标准规范。
  • "只有派生类型的实例被传递给以基类型为参数的函数"。那么,如果它总是获取派生类型,为什么还要将基类型作为参数呢?对编译器隐藏类型有什么意义?
  • 标准并没有在幕后引用是指针 - 但这是每个编译器实现它们的方式
  • @Raymond232:引用与指针非常相似,但更安全一些(一方面,因为引用不能被初始化,不像指针)。
  • @Raymond232 除了@n.m 提出的问题。您的g 函数将A 作为其参数,这使得对象在这里切片,因此,无论您传递给函数调用的类型,当它转到此函数时,它将被复制到A 对象,因此,此代码的行为将始终未定义。
【解决方案2】:

B b = (B) a 不起作用,因为没有定义转换(构造函数或转换运算符)。 B b = (B&amp;) a 起作用是因为它将a 转换为对B 的引用(由static_cast 向下转换),然后调用B 的复制构造函数。但是在这种情况下,a 不是B 的实际对象,所以这个是未定义的行为。 参见 C++ 标准中的 [expr.static.cast]

如果类型“cv1 B”的对象实际上是类型D对象的子对象,则结果指的是封闭对象 D 类型的对象。否则,行为未定义。

和 C++ 标准中的 [expr.cast] 或 http://en.cppreference.com/w/cpp/language/explicit_casthttp://en.cppreference.com/w/cpp/language/cast_operator

【讨论】:

  • 这个措辞令人困惑; C 风格的转换执行reinterpret_cast,因为它允许将任何左值转换为任何类型的引用。听起来您是在暗示 C 风格的演员正在表演 static_cast
  • 在 [expr.cast] 部分和 en.cppreference.com/w/cpp/language/explicit_cast 中,C 样式转换将按以下顺序执行 const_caststatic_caststatic_cast(带有扩展名),然后是 @987654335 @、reinterpret_castreinterpret_cast 后跟const_cast
  • @CollinDauphinee 在这种情况下,它应该执行static_cast&lt;B&amp;&gt;
【解决方案3】:

A 类有私有/公共成员。 B 类派生自 A 类,可能添加了更多私有/公共成员。

B 类“是”A 类的派生词。但是 A 类“不是”B 类的派生词。(即:您可以向下转换 A->B,但不能向上转换 B-A。)

原因是,虽然 B 是 A 的一种,但 A 不是 B 的一种,所以 B 的方法/成员不会出现(即使源代码中的方法名称相同,它也不会相同由于名称被编译器隐藏而编译的方法)。

【讨论】:

  • 这似乎无法回答问题。
  • 同意。另外,虽然明白你想说什么,但没有人会问这个问题。
猜你喜欢
  • 2011-07-15
  • 2015-05-29
  • 1970-01-01
  • 2015-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-28
  • 1970-01-01
相关资源
最近更新 更多