我有一些类型为Zoo
的不同对象,它们都有一个成员变量Collection = vector<Animal>
。我有一个动物园实例,其中所有元素都是动物,一个是所有元素都是Birds
,另一个是所有元素都是Bats
。 Birds
和Bats
都来自动物。
我希望有一种方法fly()
,我可以召唤所有的鸟类和蝙蝠,但我不确定这样做的最佳方法。我是否应该在环绕我的鸟/蝙蝠动物园时施放?像这样:
Bird thisBird = static_cast<Bird>(Collection[i]);
thisBird.fly();
...或者我可以以某种方式在Animal上有一个虚函数,它只在从它派生的类上实现?
为什么他们提供优质代码的想法和理由,欢迎!
答案 0 :(得分:1)
并非所有动物都可以飞行,因此无需为了通用目的而尝试执行特定行为(即在所有动物之间不常见)。如果你有一个动物类(或它们的衍生物)的集合,它的目的是允许它们做一个共同的行为(由每个派生类不同地实现)。
你可以实现一个FlyAble
接口(即抽象类),并且只能由能够真正飞行的动物进行扩展。然后将它们保存在为这些类型的动物指定的另一个集合中(即&lt; FlyAble
&gt;)。
答案 1 :(得分:1)
您可能不希望Bird
和Bat
这样的层次结构来自Animal
,而没有fly()
方法在较低级别中声明您正在传递的方法。这样你就松开了统一层次结构的重点。
您可以这样做:
class Animal {
virtual bool canFly() { return false; }
virtual void fly() {throw Exception( "Sorry babe, I don't fly"); }
}
并覆盖fly
和Bird
中的Bat
。
这会导致您为狗,猫实施方法fly()
,这可能是您不想要的。所以也许你可以创建一个新的类Flyer
来声明这个方法:
class Flyer : public Animal {
virtual void fly() = 0;
}
class Bat : public Flyer {}
class Bird : public Flyer {}
与Reptile
,Mammal
等更详细的生物分类不一致。
另一个技巧可能是提出类似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'
但是,如果您从Flyer
和Animal
部分课程Bird
和Birds
投放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。