在派生类C ++上访问方法的最佳方法

时间:2013-04-12 11:17:23

标签: c++ virtual base-class

我有一些类型为Zoo的不同对象,它们都有一个成员变量Collection = vector<Animal>。我有一个动物园实例,其中所有元素都是动物,一个是所有元素都是Birds,另一个是所有元素都是BatsBirdsBats都来自动物。

我希望有一种方法fly(),我可以召唤所有的鸟类和蝙蝠,但我不确定这样做的最佳方法。我是否应该在环绕我的鸟/蝙蝠动物园时施放?像这样:

Bird thisBird = static_cast<Bird>(Collection[i]);
thisBird.fly();

...或者我可以以某种方式在Animal上有一个虚函数,它只在从它派生的类上实现?

为什么他们提供优质代码的想法和理由,欢迎!

3 个答案:

答案 0 :(得分:1)

并非所有动物都可以飞行,因此无需为了通用目的而尝试执行特定行为(即在所有动物之间不常见)。如果你有一个动物类(或它们的衍生物)的集合,它的目的是允许它们做一个共同的行为(由每个派生类不同地实现)。

你可以实现一个FlyAble接口(即抽象类),并且只能由能够真正飞行的动物进行扩展。然后将它们保存在为这些类型的动物指定的另一个集合中(即&lt; FlyAble&gt;)。

答案 1 :(得分:1)

您可能不希望BirdBat这样的层次结构来自Animal,而没有fly()方法在较低级别中声明您正在传递的方法。这样你就松开了统一层次结构的重点。

您可以这样做:

class Animal {
    virtual bool canFly() { return false; }
    virtual void fly() {throw Exception( "Sorry babe, I don't fly"); }
}

并覆盖flyBird中的Bat

这会导致您为狗,猫实施方法fly(),这可能是您不想要的。所以也许你可以创建一个新的类Flyer来声明这个方法:

class Flyer : public Animal {
    virtual void fly() = 0;
}

class Bat : public Flyer {}
class Bird : public Flyer {}

ReptileMammal等更详细的生物分类不一致。

另一个技巧可能是提出类似move()的方法,而狗会将其实现为run(),将鸟实现为fly(),所有方法都采用统一的界面。

另一件事是,我认为询问狗是否可以飞行是一个有效的问题,因此我认为像Dog.canFly()这样的方法应该在你的代码中实现,作为动物的一部分。

考虑到这一切,我会选择:

// Your base animal
class Animal {
    virtual bool canFly() {return false;}
};

// Any animal that could fly, just abstract class
class Flyer {
    virtual void fly() = 0;
}

// Ants, flies etc.; real biological hierarchy
class Insect : public Animal {}
class Mammals : public Animals {}
class Birds : public Animals {}

// And now flying bird :
class Bird : public Birds, public Flyer {
    virtual bool canFly() {return true; }
    virtual void fly() {...}
}

// And flying insect
class Fly : public Insect, public Flyer {
    virtual bool canFly() {return true; }
    virtual void fly() {...}
}

然后你就可以了(基于this answer我担心你必须使用指针):

if( Collection[i]->canFly()){
    Flyer *thisBird = static_cast<Flyer*>(Collection[i]);
}

您也可以从Flyer派生Animal并使用虚拟继承,但这被称为“dreaded diamond”,它是not considered good practice,但值得一读。


正如 Ricibob 在评论中指出的那样,Flyer应该指定canFly(),这将在简单示例中显示:

class Flyer {
public:
    virtual bool canFly() const {return true;}
    virtual void fly() {cout << "Flying" << endl; }
};

Bird b;
cout << "Can fly: " << b.canFly() << endl;
// Error    1   error C2385: ambiguous access of 'canFly'

但是,如果您从FlyerAnimal部分课程BirdBirds投放Flyer

class Animal {
public:
    virtual bool canFly() const {return false;}
};

class Flyer : virtual Animal {
public:
    virtual bool canFly() const {return true;}
    virtual void fly() {cout << "Flying" << endl; }
};

class Bird : virtual public Birds, virtual public Flyer {

}; 

// Warning  1   warning C4250: 'Bird' : inherits 'Flyer::Flyer::canFly' via dominance

现在canFly()会返回1,但代码似乎对我不对。

此时......您可以为每个子类(或大型组)手动指定canFly(),也可以从Birds传递Flyers(例如,这不是正确的Chicken),或者提供新的子类:

class FlyingBrids : public Birds, public Flyer /* Flyer not delivered from Animal */ {
    virtual bool canFly() const {return true;}
};

请注意Flyer仍然很重要,因为Fly传递了Insect

答案 2 :(得分:1)

哦,是的,Animal可以有一个虚函数:

virtual void move(){}
virtual void fly(){}

当你命令一只狗飞行时它什么都不做。当您命令移动它时,它也可以调用fly()

为正在飞行的动物正确定义fly() Colection[i]->fly()会做正确的事。这样你的代码就会很简单。

但要做到这一点,你的收藏必须收集指向动物的指针,而不仅仅是动物对象。 例如:

#include <vector>
#include <memory>
#include <iostream>  
using namespace std;
struct Animal{virtual void fly(){}};
struct Bird:public Animal{void fly(){cout<<"fly\n";}};
int main()
{
  vector<unique_ptr<Animal>> Colection(1);
  Colection[0].reset( new Bird ); 
  Colection.push_back(unique_ptr<Animal>(new Bird) );
  Colection.push_back(unique_ptr<Animal>(new Animal) );
  Colection[0]->fly(); 
  Colection[1]->fly(); 
  Colection[2]->fly();   
}

阅读其他答案我可以建议您实施类似

的内容
struct Animal    {virtual bool fly(){return false;     }};
struct Bird:public Animal{bool fly(){cout<<"fly\n";return true;}};

你不需要一个单独的canFly。