【发布时间】:2015-03-19 16:53:23
【问题描述】:
我正在尝试设置一个简单的案例来解决教科书练习。代码在IDEone,下面重复。
代码是尝试存储大量动物列表的简化案例,并能够从我的包装队列中保存的这些动物特定列表之一返回任何通用动物。
我可以很好地添加动物,并且在尝试检索狗时,我似乎得到了指向狗的指针,因为我可以打印它的 name。但是,如果我尝试调用它的成员函数speak(),代码会崩溃,我不知道为什么。
class Animal {
public:
virtual void speak() = 0;
string name;
};
class Dog: public Animal {
public:
Dog(string n) { this->name=n; }
void speak() { cout<<name<<" says WOOF!"<<endl; }
};
class AnimalQueue {
list<Dog> dogs;
list<Cat> cats; // etc.
public:
void enqueue(Animal* a) {
Dog * d = dynamic_cast<Dog*>(a);
if (d!=nullptr) dogs.push_back(*d);
else // check for other animals, etc.
}
Dog* dequeueDog() {
Dog * d = &(dogs.front());
dogs.pop_front();
return d;
}
Animal dequeueAny() {
// Should return a random animal from any list
}
};
int main() {
// Set up
AnimalQueue q;
Dog * d;
d = new Dog("Rex");
q.enqueue(d);
// Retrieve Rex
d = q.dequeueDog();
cout<<d->name<<endl; // Prints "Rex"
d->speak(); // Crashes?!
return 0;
}
编辑:抱歉,在削减我的代码时,我消除了问题的本质,即我应该能够将Animal 的任何子类添加到我的列表中,并且有一个名为@987654326 的特殊函数@ 应该能够从任何列表中返回一个随机的 Animal。代码已被编辑以包含此内容(以及我之前省略的 nullptr 检查何时 enqueueing。
处理此问题的最佳方法是什么?是否传递对现有Animal 对象的引用?那行得通吗?即可以,我有:
void dequeueAny(Animal * a) {
// for example, let's return a Dog
Dog d = dogs.front();
dogs.pop_front();
*a = d;
}
诚然,dequeueDog() 之类的东西可能应该按值返回 Dog。
【问题讨论】:
-
为什么 Animal 没有一个构造函数,它的名字是 dog 调用的?如果
enqueue只能合理处理狗,那你为什么不把它的参数设为Dog *呢?您的代码也会泄漏内存。 -
@NeilKirk:在这种情况下,应该将其重命名为
enqueueDog(),以匹配dequeueDog()。 -
如果将不是
Dog的Animal传递给enqueue(),dynamic_cast将返回一个空指针,您将其存储在列表中,但您不检查任何地方。并且dequeueDog()也不会在弹出之前检查列表是否为空。 -
考虑
std::list<std::shared_ptr<Dog/Animal>> -
尼尔是对的。
main()正在泄漏d,因为enqueue()复制 动物不理会原件,而main()没有调用delete d。这段代码绝对应该使用std::list<std::shared_ptr<Animal>>(或std::list<std::unique_ptr<Animal>>),并停止复制所有内容。
标签: c++ pointers polymorphism