downcasting是否打败了多态性的目的? [英] Does downcasting defeat the purpose of polymorphism?

查看:154
本文介绍了downcasting是否打败了多态性的目的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



这是一个伪代码示例,我得到的是:

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


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


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



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

for(int i = 0; i< cars.size(); ++ i){
if兰博基尼* lambo = dynamic_cast<兰博基尼*>(cars [i])){
lambo-> retractTheRoof();
};
goFast()
};
};
};

在示例中,有一个 RichGuy 类。 Richguy 仅在单个向量中追踪他的 Cars 。因为他有这么多的汽车根据他们是否是一个 FordFocus 来跟踪他们是太麻烦了或$ code>兰博基尼。然而,他拥有可伸缩屋顶的唯一一种类型的车就是兰博。为了 shrinkTheRoof() RichGuy 现在必须确定是否 Car 他确实是一个 Lamboghini ,然后downcast执行这个功能。



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

解决方案

现在如果有多种类型的汽车是可伸缩的,说这样的汽车是 CarA CarB CarC (除了
$ b

 如果(/ code> Lamborghini ),那么你要写这个:兰博基尼* lambo = dynamic_cast<兰博基尼*>(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 并从中派生:

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

class兰博基尼: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]))
{
可伸缩 - > retractTheRoof(); //多态调用!
}

酷?不是吗?



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



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


$ b $程序到界面,而不是实现。 (Gang of Four 1995:18)


另请参见:








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

  class Car {
public:
virtual〜Car(){} // important:virtual destructor
virtual int goFast()= 0;
};

它的重要因素是您维护 Car * ,这意味着,以后你想通过基类指针删除实例,为此,你需要使一个虚拟析构函数来执行〜Car()否则 delete car [i] 将调用未定义的行为。


I encountered a question today, found here, which raised this question for me.

Here's a pseudo-code example of what I'm getting at:

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

In the example, there is a RichGuy class. Richguy only keeps track of his Cars in a single vector. Because he has so many Cars it would be too troublesome to keep track of them based on if they're a FordFocus or a Lamborghini. However, the only type of car he has with a retractable roof is the Lambo. In order to retractTheRoof(), RichGuy must now determine if the Car he has is indeed a Lamboghini, and then downcast to execute this function of it.

Based on this example, was the choice to downcast in good design? Or did it violate the purpose of polymorphism, assuming that purpose is to allow derived classes to define their own behavior, and provide a common interface for classes like RichGuy? And if so, is there a better way to allow for functions like retractTheRoof() (or at least it's effect) to be available to RichGuy to use?

解决方案

Now if there are more than one type of cars which is retractable, say such cars are CarA, CarB, and CarC (in addition to Lamborghini), then are you going to write this:

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

So a better design in such cases would be this: add an interface called IRetractable and derive from it as well:

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 {
   //...
}; 

Then you can simply write this:

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

Cool? Isn't it?

Online demo : http://www.ideone.com/1vVId

Of course, this still uses dynamic_cast, but the important point here is that you're playing with interfaces only, no need to mention concrete class anywhere. In other words, the design still makes use of runtime-polymorphism as much as possible. This is one of the principle of Design Patterns:

"Program to an 'interface', not an 'implementation'." (Gang of Four 1995:18)

Also, see this:


Other important point is that you must make the destructor of Car (base class) virtual:

class Car{
public:
    virtual ~Car() {} //important : virtual destructor
    virtual int goFast() = 0;
};

Its imporant because you're maintaining a vector of Car*, that means, later on you would like to delete the instances through the base class pointer, for which you need to make ~Car() a virtual destructor, otherwise delete car[i] would invoke undefined behaviour.

这篇关于downcasting是否打败了多态性的目的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆