我今天遇到了一个问题found here,它为我提出了这个问题。

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

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();
        };
    };
};

在示例中,有一个RichGuy类。 Richguy仅在单个 vector 中跟踪其Cars。由于他的Cars太多,因此很难根据它们是FordFocus还是Lamborghini来跟踪它们。但是,他唯一具有可伸缩车顶的汽车是Lambo。为了retractTheRoof()RichGuy现在必须确定他拥有的Car是否确实是Lamboghini,然后向下转换以执行此功能。

根据此示例,是否选择精打细算?还是它违反了多态性的目的,假设目的是允许派生类定义自己的行为,并为RichGuy等类提供通用接口(interface)?如果是这样,是否有更好的方法允许retractTheRoof()使用类似RichGuy的功能(或至少它的作用)?

最佳答案

现在,如果有多种类型的可伸缩汽车,比如说这些汽车是CarACarBCarC(除了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();
}

因此,在这种情况下,更好的设计是:添加一个名为IRetractable的接口(interface)并从中派生:
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 {
   //...
};

然后,您可以简单地编写以下代码:
if(IRetractable *retractable =  dynamic_cast<IRetractable *>(cars[i]))
{
    retractable->retractTheRoof(); //Call polymorphically!
}

凉?是不是

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

当然,这仍然使用dynamic_cast,但是此处的要点是,您只在使用接口(interface),而无需在任何地方提及具体的类。换句话说,设计仍然尽可能多地使用运行时多态。这是Design Patterns的原理之一:



另外,请参阅以下内容:
  • What does it mean to "program to an interface"?


  • 另一个重要的一点是,必须将Car(基类)的析构函数设为虚拟:
    class Car{
    public:
        virtual ~Car() {} //important : virtual destructor
        virtual int goFast() = 0;
    };
    

    这很重要,因为您要维护Car*的 vector ,这意味着以后您想通过基类指针删除实例,为此您需要使~Car()成为虚拟析构函数,否则delete car[i]会调用未定义的行为。

    07-27 13:21