给出一个具有父类 A 和子类 B 的参数 x 的函数,该子类继承自 A ,当它是 B 对象时,如何访问 x 属性的正确方法?上面的示例如下所示。
-编辑-
class A {
}
class B : public A {
public:
int foo;
}
void bar(A foo_){
cout << foo_.foo << endl;
}
int main(){
// Previous code:
// A a{};
// bar(a);
// Current code:
B b{}; // @O'Neil: B b(); will lead to the most vexing parse problem
bar(b);
}
-编辑-
衷心感谢您的帮助。
答案 0 :(得分:0)
如果将b
传递到bar()
而不进行任何其他更改,则bar()
只能访问其参数内的A
,这是一个Object Slicing问题。
解决方案是通过引用或通过指针将b
传递到bar()
中,即使参数的类型为A
,也可以使用dynamic_cast<>
来尝试然后返回派生对象。
按如下所示进行传递:
bar(&b);
并像这样重写bar
:
void bar(A *foo_)
{
B *foo_as_B = dynamic_cast<B *>(foo_);
if (foo_as_B != nullptr)
{
cout << foo_as_B->foo << endl;
}
}
然后它将起作用。
dynamic_cast<>
能够将超类的指针/引用转换为子类的指针/引用,前提是该超类对象实际上最初是作为子类实例化的。它实际上比这要复杂一些,但是仅仅入门就足够了。
在指针的情况下,如果强制转换正确,则将获得有效的指针。如果无法完成转换,则指针版本将返回nullptr。因此,在使用前进行检查的原因。
良好的防御性编码要求您应始终检查来自未知来源的指针。即使您可能认为强制转换会成功完成并且在编写bar()时可能是对的,但在生产代码中,很可能其他程序员会在两三年后出现并添加以下内容:
class C : public A
{
....
}
C c{};
bar(&c);
此时,对nullptr的检查将使您摆脱未定义行为的恶魔。
您也可以使用引用来执行此操作,但是此处的失败情况有所不同:它引发了一个异常,您可能想捕获该异常。
This question显示了此细节。
-编辑-
为回应有关出现错误C2683: 'dynamic_cast': 'A' is not a polymorphic type
的说明,this SO Question解释了原因。该标准规定,dynamic_cast<>
必须至少有一个虚函数才能起作用,这就是多态的意思。
我从来没有碰过这个问题,因为对于我来说,在生产代码中使所有析构函数虚拟化是一种反思性的行动。为什么要使析构函数虚拟化的问题是一个完全独立的主题,我在这里不做深入探讨。只需说这样做确实有其优势即可。
总而言之,解决方法是更改class A
如下:
class A
{
public:
virtual ~A() {}
};
答案 1 :(得分:0)
根据this link,它应该已经从基类处理到多态类的派生指针,并且它确实属于RTTI,但是仍然会出错。
答案 2 :(得分:0)
该线程的最终解决方案可以找到here。感谢所有参与者。