在一个数据结构中存储多态类型

时间:2014-04-02 09:46:32

标签: c++ oop

你有一个动物收容所。避难所可以存储可变数量的动物。你把许多动物(狗和猫)放进了避难所。

然后你告诉员工随机选择并带给你几只动物。你不知道他选择了什么类型的动物。 你告诉他们说话。其中一些是“吠”,一些是“喵”。

重要!狗可以取,猫不能。 如果你确定你已经选择了一只狗它应该能够立即取出(例如没有从动物到狗的向上转播)

如何实现这种逻辑? (最好不要加速::任何)

以下是部分工作示例:http://ideone.com/kR4788

#include <iostream>
#include <map>
using namespace std;

class Animal {};

class Shelter {
    private:
        std::map<int, Animal*> animals;
    public:
        void Add(Animal* animal) {
            animals[animals.size()] = animal;
        };
        Animal* Select(int index) {
            return animals[index];
        }
};

class Dog: public Animal {
    public:
        void Speak() { cout << "bark" << endl; }
        void Fetch() {}
};

class Cat: public Animal {
    public:
        void Speak() { cout << "meow" << endl; }
};

Shelter shelter;

int main() {
    shelter.Add(new Cat());
    shelter.Add(new Dog());

    // I'd like to make it work like this
    //
    // shelter.Select(0)->Speak(); /* meow */
    // shelter.Select(1)->Speak(); /* bark */
    //
    // Like below but without upcasting to given animal

    ((Cat*) shelter.Select(0))->Speak();
    ((Dog*) shelter.Select(1))->Speak();

    // I know under index 1 is a Dog so it can fetch!
    //
    // shelter.Select(1)->Fetch(); /* no segfault */
    //
    // Like below but without upcasting to given animal
    ((Dog*) shelter.Select(1))->Fetch();


    return 0;
}

4 个答案:

答案 0 :(得分:1)

编辑:

您可以尝试使用dynamic_castAnimal对象转换为Dog,然后调用fetch方法:

Dog *foo = dynamic_cast<Dog*>(shelter.Select(1));
if (foo) {
  foo->Fetch();
}

如果dynamic_cast失败,它将返回null,因此请确保在使用之前检查对象是否不是null。有关dynamic_cast的详细信息,请查看here


您可以在Animal界面添加virtual功能:

class Animal {
  public:
    virtual void speak();
};

此外,根据不相关的说明,您的speak方法似乎无法修改对象,因此您应该考虑将它们设为const

class Animal {
  public:
    virtual void speak() const;
};

您可以找到有关const-correctness here的更多信息。

答案 1 :(得分:1)

Aliou注意speak应该在virtual中声明Animal,否则层次结构相当无用,换句话说,就没有多态性。

使用Animal测试Dog是否为dynamic_cast<Dog*>(并同时向上转播)是一个需要考虑的选项。不漂亮,但它确实有效。

Dog *dog = dynamic_cast<Dog*> shelter.Select(1);
if (dog) dog->Fetch();

dynamic_cast指针从不抛出,正如其他人建议的那样......)

另一个解决方案是在virtual中定义Fetch Animal,也许作为NOP({}),因此您不必在动物中定义它不要拿。

答案 2 :(得分:0)

根据要求,我发表评论作为答案。完整的来源位于Ideone

在动物类:

class Animal {
  public:
    virtual void Speak() const = 0;
    virtual void Fetch() const { cout << "This animal can't fetch" << endl;};

    virtual ~Animal(){ }
};

需要虚拟析构函数来确保为从基类派生的对象调用正确的析构函数。如果没有虚析构函数,则只调用基类析构函数,而不是派生对象的析构函数。

在狗:

void Fetch() const { cout << "fetch" << endl; }

主要是:

shelter.Select(0)->Speak();
shelter.Select(1)->Speak();

shelter.Select(0)->Fetch();
shelter.Select(1)->Fetch();

答案 3 :(得分:0)

你没有要求雇员只返回可以取的动物,因此你必须检查(施放)每只动物可以取的动物。

另一种方法是向动物添加一个虚拟提取功能,除了狗之外什么都不做。