2

假设我有以下类结构。我希望能够确定我的 Animal 向量中的元素是什么类类型,以便我可以对其执行特定于子类的方法。下面的示例应演示:

#include <iostream>
#include <vector>

using namespace std;

class Animal {
    public:
    int foodcount;

    Animal() {
        foodcount = 0;
        cout << "An animal was created.\n";
    }
    virtual ~Animal() {
        cout << "An animal was destroyed.\n";
    }
};

class Lion : public Animal {
    public:
    Lion() {
        cout << "A lion was created.\n";
    }
    virtual ~Lion() {
        cout << "A lion was destroyed.\n";
    }
    void chowMeat(int howmuch) {
        foodcount += howmuch;
    }
};

class Butterfly : public Animal {
    public:
    Butterfly() {
        cout << "A butterfly was created.\n";
    }
    virtual ~Butterfly() {
       cout << "A butterfly was destroyed.\n";
    }
    void drinkNectar(int howmuch) {
       foodcount += howmuch;
    }
};

int main() {
    Animal* A = new Lion();
    Animal* B = new Butterfly();
    vector<Animal*> v;

    v.push_back(A);
    v.push_back(B);

    // a little later

    for (int i=0; i<v.size(); i++) {
        if (v[i] is a Lion) v[i]->chowMeat();  // will not work of course
        if (v[i] is a Butterfly) v[i]->drinkNectar();   // will not work of course
    }

    std::cin.get();
    return 0;
}

显然标记的代码不起作用,但是我该怎么做呢?是否有我应该遵循但不是的解决方法或设计原则?我已经研究了 dynamic_cast 但明白这很不漂亮。那么我应该如何正确地做到这一点呢?

在 Java 中,我会这样做:

if (v.get(i).getClass() == Lion.class) {
    ((Lion)v.get(i)).chowMeat();
}
if (v.get(i).getClass() == Butterfly.class) {
    ((Butterfly)v.get(i)).drinkNectar();
}
4

4 回答 4

1

理想情况下,您将向基类添加一个虚函数,void eat(int quantity)并在派生类中覆盖该函数。

在这种情况下,将函数设为非虚拟函数并在基类中实现它甚至可能是有意义的,因为两个派生类都做完全相同的事情。

除此之外,您可以使用dynamic_cast测试对象的动态类型:

if (Lion* lion = dynamic_cast<Lion*>(v[i])) {
    lion->chowMeat(42); 
}
else if (Butterfly* butterfly = dynamic_cast<Butterfly*>(v[i])) {
    butterfly->drinkNectar(42);
}
// etc.

(另一方面,您需要非常小心地在 C++ 中使用裸指针;在手动管理资源的情况下编写正确的代码非常困难。在您的示例中,您没有释放 and 指向的对象AB并且因此泄露了它们。考虑使用智能指针,例如shared_ptr,自动管理您的资源。)

于 2010-12-08T00:42:10.813 回答
0

循环的目的是什么?是用来吃东西的吗?在这种情况下,将 a 添加virtual void consumeFood(int howMuch)到您的基类中,并在派生类中覆盖它。

于 2010-12-08T00:41:27.163 回答
0

如果该示例真正具有代表性,那么虚函数将更巧妙地解决您的直接问题。

无论如何,如果您的类具有虚函数,最简单的答案是用于dynamic_cast检查对象是否属于给定类型。例如:

for (int i=0; i<v.size(); i++) {
    if (Lion *lion = dynamic_cast<Lion *>(v[i]))
        lion->chowMeat();
    else if(Butterfly *butterfly = dynamic_cast<Butterfly *>(v[i]))
        butterfly->drinkNectar();
}

它内置于语言中,只是用于检查指向基址的指针是否实际上指向派生类型更多的对象。

另一种选择是在您的基类中使用某种虚拟 GetType 函数,您可以覆盖每个类以返回唯一标识该类的内容(整数、对象等)。然后在运行时调用此函数,并检查结果以找出所指向的对象类型。

dynamic_cast具有内置于语言中的优势,并且不需要您付出任何努力来支持。使用自己的函数在各种编译器中具有更可预测的性能特征,并且允许存储数据而不仅仅是对象真正的类型——但您必须自己编写。

于 2010-12-08T01:16:50.730 回答
0

为什么不吃()?

class Animal {
    public:
    int foodcount;

    Animal() {
        foodcount = 0;
        cout << "An animal was created.\n";
    }
    virtual ~Animal() {
        cout << "An animal was destroyed.\n";
    }
    virtual void eat(int howMuch) {
        foodcount += howmuch;
    }
};

class Lion : public Animal {
    public:
    virtual void eat(int howmuch) {
        Animal::eat(howmuch + 19);
    }
};

class Butterfly : public Animal {
    void eat(int howmuch) {
       Animal::eat(howmuch / 1000);
    }
};

class Tribble: public Animal
{
    void eat(int howmuch) {
       throw DontFeedTribles();
    }
};

int main() {
    std::auto_ptr<Animal> A = new Lion();
    std::auto_ptr<Animal> B = new Butterfly();
    vector<Animal*>  menagerie;

    menagerie.push_back(A.get());
    menagerie.push_back(B.get());

    BOOST_FOREACH(Animal* animal, menagerie)
    {
        animal->eat(10000);
    }

    std::cin.get();
    return 0;
}
于 2010-12-08T01:02:20.070 回答