【问题标题】:C++ Polymorphism: from parent class to child [duplicate]C ++多态性:从父类到子类[重复]
【发布时间】:2014-09-11 03:07:30
【问题描述】:

在C++中我们可以将子类指针转换为父类,但是有什么办法可以将其转换回来:从父类中,从子类中获得,将子类返回给子类?

我的意思是:

class Parent
{
    ...
};

class Child : public Parent
{
    ...
};

int main(int argc, char const *argv[])
{
    Child* child = new Child();
    Parent* parent = child;
    Child* old_child = parent; // how to do this??
    return 0;
}

感谢您的回答。

【问题讨论】:

  • Child* old_child = dynamic_cast<Child*>(parent);
  • 是的。 static_cast<> and dynamic_cast<> 可用于执行此操作。
  • @david.pfx 我已经麻烦将其标记为重复,所以只需将链接应用为评论。
  • 需要这样做通常表明设计不好。您应该有一个可从父指针调用的虚函数。
  • Child* old_child = child;

标签: c++ oop polymorphism


【解决方案1】:

您需要将cast 对象返回给子级。这样做是这样的:

 Child * old_child = static_cast<Child*>(parent);

 Child * old_child = dynamic_cast<Child*>(parent);

【讨论】:

  • 您是否介意解释一下这两种强制转换方法的区别和实际行为。
【解决方案2】:
int main() { 
   Parent parent;
   Child child;

   // upcast - implicit type cast allowed
   Parent *pParent = &child; 

   // downcast - explicit type case required 
   Child *pChild = (Child *) &parent;
}

您应该使用dynamic_cast 安全地执行此操作:

Child *p = dynamic_cast<Child *>(pParent)

编辑

如果类型不是基类的一部分,dynamic_cast 将返回一个空指针,同时转换为引用也会引发 bad_cast 异常。如果您不知道对象类型是什么,dynamic_cast 特别有用。

另一方面static_cast:

Child *p = static_cast<Child *>(pParent)

这假定您要反转显式转换并且不执行运行时检查。这允许灵活性,但必须谨慎使用。

如上所示的常规向下转型:

Child *pChild = (Child *) &parent;

是一种 C 风格的向下转换(如 static_cast),它也可能转换为私有基类(不确定,多重继承),而 static_cast 会导致编译时错误。 numeric 转换之类的东西就是一个很好的例子。

【讨论】:

  • 更新答案以解释一些安全问题
【解决方案3】:

"但是有什么办法可以将它转换回来:从父级,从子级获得,还给子级?"

是的,正如其他答案中所述,有两种方法可以做到这一点。

Child * old_child = dynamic_cast<Child*>(parent);

dynamic_cast&lt;&gt; 的结果可以在运行时检查,因此您可以确定parent 对象是否真的代表Child 实例:

if(!old_child) {
     // parent is not a Child instance
}

还请注意,要使其正常工作,有问题的类需要有一个 vtable,RTTI 可以实际确定它们的关系。实现这一点的最简单形式是为Parent 类提供一个虚拟析构函数

class Parent {
public:
    virtual ~Parent() {}
    // or
    // virtual ~Parent() = default;
    // as suggested for latest standards
};

注意:
如果这应该适用于一般的设计决策,我会强烈地忽略它。改用pure virtual interfaces,保证实现或不实现。


static_cast&lt;&gt; 的第二种方式可以用在环境中,你很清楚parent 实际上是一个孩子。最简单的形式是CRTP,其中Parent 将继承类作为模板参数

template <class Derived>
class Parent {

     void someFunc() {
         static_cast<Derived*>(this)->doSomething();
     }
};

class Child : public Parent<Child> {
public:
    void doSomething();
};

将在编译时检查Parent&lt;&gt;static_cast&lt;&gt; 实例化的有效性。

注意:
另一个优点是您可以使用派生接口,该接口使用

  • Derived 的静态类成员
  • typedefDerived 提供
  • ...更多类特征,可以在编译时检查

【讨论】:

  • dynamic_cast 的结果可以是……如果基类是真正的多态类型,也就是至少有一个虚函数或基类。否则,它会降级为 static_cast(在任何情况下证明正确时,它都会降低强度为 static_cast)。
  • @Deduplicator THX!现在好点了吗?
  • 是的,+1。顺便说一句:使用= default; 而不是{} 作为 dtor 主体可能会稍长一些,但并不妨碍该类变得微不足道,并带来所有好处(尽管它需要一个更现代的编译器)。
【解决方案4】:

以上答案都不错,概念上你可以这样想,

您的 Car 对象派生自 Vehicle 类。您可以将Car 引用为Vehicle,并且可以转换为Car,因为它原本属于Car。但如果您的Vehicle 对象实际上代表Bike 并试图转换为Car,这将是一个问题。这就是您需要安全投射的原因。

class Vehicle
{
    ...
};

class Car : public Vehicle
{
     ...
};

class Bike : public Vehicle
{
     ...
};


int main(int argc, char const *argv[])
{
     Vehicle* vehicle = new Car();
     Car* old_car = dynamic_cast<Car *>(vehicle); 
     if(old_car) {
         // OK Vehicle is Car, use it
     }

     vehicle = new Bike();
     old_car = dynamic_cast<Car *>(vehicle); 
     if(old_car) {
         // No Vehicle isn't Car, this code won't execute
     }

     return 0;
}

【讨论】:

  • 除了用Vehicle/Car 隐喻替换Parent/Child 之外,这个答案在哪些方面增加了任何价值?
猜你喜欢
  • 2011-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-01
  • 1970-01-01
  • 2021-12-26
  • 1970-01-01
相关资源
最近更新 更多