向下转换是否会破坏多态性的目的?

Ann*_*inn 17 c++ oop polymorphism

我今天在这里找到了一个问题,它为我提出了这个问题.

这是我所得到的伪代码示例:

class Car{
public:
    virtual int goFast() = 0;
};


class FordFocus : public Car {
public:
    int goFast(){
        return 35;
    };
};


class Lamborghini : public Car {
    bool roof;
public:
    int goFast(){
        return -1/0;  // crash 
    };
    void retractTheRoof(){
        roof = 0;
    };
};



class RichGuy {
    vector<Car *> cars;
public:
    void goDrive() {

        for(int i = 0; i < cars.size(); ++i) {
            if(Lamborghini* lambo = dynamic_cast<Lamborghini*>(cars[i])) {
                lambo->retractTheRoof();
            };
            goFast();
        };
    };
};
Run Code Online (Sandbox Code Playgroud)

在示例中,有一个RichGuy类. RichguyCars在一个向量中跟踪他.因为他有这么多Cars,所以根据他们是一个FordFocus还是一个来跟踪它们会很麻烦Lamborghini.然而,他有一个可伸缩屋顶的唯一类型的汽车是兰博.为了retractTheRoof(),RichGuy现在必须确定Car他是否确实是一个Lamboghini,然后向下转发执行它的这个功能.

基于这个例子,是否选择了良好的设计?或者它是否违反了多态的目的,假设目的是允许派生类定义自己的行为,并为类提供通用接口RichGuy?如果是这样,是否有更好的方法可以使用retractTheRoof()(或至少它的效果)等功能RichGuy

Naw*_*waz 14

现在,如果有不止一种类型的汽车是可伸缩的,说这样的车CarA,CarBCarC(除Lamborghini),那么你要这样写:

if(Lamborghini* lambo = dynamic_cast<Lamborghini*>(cars[i])) {
    lambo->retractTheRoof();
}
else if(CarA * pCarA = dynamic_cast<CarA*>(cars[i])) {
    pCarA->retractTheRoof();
}
else if(CarB * pCarB = dynamic_cast<CarB*>(cars[i])) {
    pCarB->retractTheRoof();
}
else if(CarC * pCarC = dynamic_cast<CarC*>(cars[i])) {
    pCarC->retractTheRoof();
}
Run Code Online (Sandbox Code Playgroud)

因此,在这种情况下更好的设计是:添加一个名为的接口IRetractable并从中派生出来:

struct IRetractable 
{
   virtual void retractTheRoof() = 0;
};

class Lamborghini : public Car, public IRetractable {
   //...
};

class CarA : public Car, public IRetractable {
   //...
};
class CarB : public Car, public IRetractable { 
   //...
};
class CarC : public Car, public IRetractable {
   //...
}; 
Run Code Online (Sandbox Code Playgroud)

然后你可以简单地写这个:

if(IRetractable *retractable =  dynamic_cast<IRetractable *>(cars[i])) 
{
    retractable->retractTheRoof(); //Call polymorphically!
}
Run Code Online (Sandbox Code Playgroud)

凉?不是吗?

在线演示:http://www.ideone.com/1vVId

当然,这仍然使用dynamic_cast,但重要的一点是你只使用接口,无需在任何地方提及具体类.换句话说,设计仍然尽可能地使用运行时多态性.这是设计模式的原则之一:

"编程到'界面',而不是'实现'." (Gang of Four 1995:18)

另外,看到这个:


其他重要的一点是你必须使Car(基类)虚拟的析构函数:

class Car{
public:
    virtual ~Car() {} //important : virtual destructor
    virtual int goFast() = 0;
};
Run Code Online (Sandbox Code Playgroud)

它很重要,因为你正在维护一个向量Car*,这意味着,稍后你想通过基类指针删除实例,你需要为它做~Car()一个虚拟析构函数,否则delete car[i]会调用未定义的行为.

  • 谢谢!啊哈,不要轻视其他答案,但是你的第一个直接回答标题中提出的问题. (2认同)

Pup*_*ppy 7

是的,这通常是更好的设计案例.如果对所有派生类都有意义,则只应在层次结构中引入虚函数.

但是,你的MODEL枚举完全没有价值 - 这dynamic_cast就是实际的目的.

if(Lamborghini* lambo = dynamic_cast<Lamborghini*>(cars[i])) {
    lambo->retractTheRoof();
}
Run Code Online (Sandbox Code Playgroud)

  • 当福特成为一辆敞篷车时,你当然应该到处运动一个动态演员到兰博,并在那里添加福特.尼斯.非常OOP.没有低估,因为你的观点是`dynamic_cast`的正确用法,但它不应该是一个答案. (2认同)