关于oop:C ++ Polymorphism:从父类到子类

C++ Polymorphism: from parent class to child

本问题已经有最佳答案,请猛点这里访问。

在C++中,我们可以将子类指针转换为父级,但是有没有方法可以将它转换回:从父级中得到的父级,给子类返回?

我的意思是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
}

谢谢你的回答。


"but is there any way to convert it back: from parent, which was obtained from child, give child class back?"

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

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

可以在运行时检查dynamic_cast<>的结果,从而确定parent对象是否真的代表Child实例:

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

还要注意,为了使这个工作正常,所讨论的类需要有一个vtable,rtti实际上可以确定它们的关系。实现这一点的最简单形式是给parent类一个虚析构函数

1
2
3
4
5
6
7
class Parent {
public:
    virtual ~Parent() {}
    // or
    // virtual ~Parent() = default;
    // as suggested for latest standards
};

注:如果这适用于一般的设计决策,我会坚决无视它。使用纯虚拟接口来代替,不管是否保证要实现。

第二种方法static_cast<>可以用于环境中,您很清楚parent实际上是一个孩子。最简单的形式是crtp,其中parent将继承类作为模板参数。

1
2
3
4
5
6
7
8
9
10
11
12
template <class Derived>
class Parent {

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

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

将在编译时检查Parent<>static_cast<>的安装有效性。

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

  • Derived的静态类成员
  • Derived提供的typedef
  • …更多的类特性,可以在编译时检查


1
2
3
4
5
6
7
8
9
10
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安全地执行此操作:

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

编辑

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

另一方面,static_cast

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

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

上面所示的常规向下投射:

1
Child *pChild = (Child *) &parent;

是C样式的向下强制转换(如static_cast类型),它也可以强制转换为私有基类(不确定是否存在多重继承),而static_cast类型将导致编译时错误。像数值转换之类的东西就是一个很好的例子。


您需要将对象强制转换回child。这样做:

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

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


上面的答案很好,从概念上来说你可以这样想,

您的Car对象,它来自Vehicle类。您可以将Car称为Vehicle,也可以转换为Car,因为它原来属于Car。但如果你的Vehicle对象实际代表Bike并试图转换为Car的话,这将是一个问题。所以你需要安全的施法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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;
}