C++多态指针不能调用成员函数
C++ polymorphic pointer cannot invoke member function
我正在尝试设置一个简单案例来解决教科书练习。代码在 IDEone 上,并在下面重复。
该代码是一个简化的案例,它试图存储许多动物列表,并且能够 return 我的包装队列中保存的这些动物特定列表之一中的任何通用动物。
我可以很好地添加动物,并且在尝试找回狗时,我似乎得到了指向狗的指针,因为我可以打印它 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
的任何子 class 添加到我的列表中,并且有一个名为 dequeueAny()
的特殊函数,它应该能够从任何列表中 return 随机 Animal
。代码已被编辑以包含此内容(以及我之前省略的 nullptr
检查 enqueue
ing.
处理此问题的最佳方法是什么?是传入对现有Animal
对象的引用吗?那行得通吗?即可以,我有:
void dequeueAny(Animal * a) {
// for example, let's return a Dog
Dog d = dogs.front();
dogs.pop_front();
*a = d;
}
诚然,dequeueDog()
之类的值可能 return 一个 Dog
。
Dog* dequeueDog() {
Dog * d = &(dogs.front());
dogs.pop_front();
return d;
}
你正在获取一个指向列表前面项目的指针,删除那个项目(通过调用pop_front
)然后返回一个悬空指针。
您正在返回存储的元素的地址(list::front returns a reference and you're taking the address of it) and then popping it (list::pop_front 销毁 对象):
Dog* dequeueDog() {
Dog * d = &(dogs.front()); // Take the address of the front object
dogs.pop_front(); // Destroy the front object
return d; // Return the address to the deleted object (unsafe state)
}
通过该指针访问内存是undefined behavior。
一个可能的解决方案是按值返回您的对象
class Animal {
public:
virtual void speak() = 0;
virtual ~Animal() {}; // Always a good thing if the class has virtual members
string name;
};
class Dog : public Animal {
... // unchanged
};
class AnimalQueue {
list<Dog> dogs;
public:
void enqueue(Animal* a) {
Dog * d = dynamic_cast<Dog*>(a);
dogs.push_back(*d); // This creates a copy of d and stores it
}
Dog dequeueDog() {
Dog d = dogs.front(); // This creates a copy of the front element
dogs.pop_front(); // Destroy the front element
return d;
}
};
int main() {
AnimalQueue q;
Dog * d;
d = new Dog("Rex");
q.enqueue(d);
*d = q.dequeueDog();
cout << d->name << endl;// Prints "Rex"
d->speak(); // Prints WOFF
delete d; // Free your memory
return 0;
}
请注意,您还忘记释放内存,从而导致内存泄漏。要么使用 smart pointer,要么释放你的记忆,做一个好公民。
编辑:OP 编辑了他的问题以指定他还需要一个 dequeueAny
方法。我强烈建议您在回答问题后不要编辑您的问题(要求应尽可能静态)。无论如何,在那种情况下我建议你使用指针(或智能指针)而不是复制对象
class AnimalQueue {
std::list<Animal*> animals;
public:
void enqueue(Animal* a) {
animals.push_back(a); // Copy the pointer
}
Animal* dequeue() {
Animal *d = animals.front();
animals.pop_front(); // Destroy the pointer
return d;
}
};
int main() {
AnimalQueue q;
std::unique_ptr<Dog> d = std::make_unique<Dog>("Rex");
q.enqueue(d.get()); // This will now store the pointer to the object
// Try with dog-specific command
Animal *sameDog = q.dequeue(); // d.get() and sameDog are now pointing at the same object
if (d.get() == sameDog)
std::cout << "d.get() == sameDog" << std::endl;
std::cout << sameDog->name << std::endl;// Prints "Rex"
sameDog->speak();
return 0;
}
我正在尝试设置一个简单案例来解决教科书练习。代码在 IDEone 上,并在下面重复。
该代码是一个简化的案例,它试图存储许多动物列表,并且能够 return 我的包装队列中保存的这些动物特定列表之一中的任何通用动物。
我可以很好地添加动物,并且在尝试找回狗时,我似乎得到了指向狗的指针,因为我可以打印它 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
的任何子 class 添加到我的列表中,并且有一个名为 dequeueAny()
的特殊函数,它应该能够从任何列表中 return 随机 Animal
。代码已被编辑以包含此内容(以及我之前省略的 nullptr
检查 enqueue
ing.
处理此问题的最佳方法是什么?是传入对现有Animal
对象的引用吗?那行得通吗?即可以,我有:
void dequeueAny(Animal * a) {
// for example, let's return a Dog
Dog d = dogs.front();
dogs.pop_front();
*a = d;
}
诚然,dequeueDog()
之类的值可能 return 一个 Dog
。
Dog* dequeueDog() {
Dog * d = &(dogs.front());
dogs.pop_front();
return d;
}
你正在获取一个指向列表前面项目的指针,删除那个项目(通过调用pop_front
)然后返回一个悬空指针。
您正在返回存储的元素的地址(list::front returns a reference and you're taking the address of it) and then popping it (list::pop_front 销毁 对象):
Dog* dequeueDog() {
Dog * d = &(dogs.front()); // Take the address of the front object
dogs.pop_front(); // Destroy the front object
return d; // Return the address to the deleted object (unsafe state)
}
通过该指针访问内存是undefined behavior。
一个可能的解决方案是按值返回您的对象
class Animal {
public:
virtual void speak() = 0;
virtual ~Animal() {}; // Always a good thing if the class has virtual members
string name;
};
class Dog : public Animal {
... // unchanged
};
class AnimalQueue {
list<Dog> dogs;
public:
void enqueue(Animal* a) {
Dog * d = dynamic_cast<Dog*>(a);
dogs.push_back(*d); // This creates a copy of d and stores it
}
Dog dequeueDog() {
Dog d = dogs.front(); // This creates a copy of the front element
dogs.pop_front(); // Destroy the front element
return d;
}
};
int main() {
AnimalQueue q;
Dog * d;
d = new Dog("Rex");
q.enqueue(d);
*d = q.dequeueDog();
cout << d->name << endl;// Prints "Rex"
d->speak(); // Prints WOFF
delete d; // Free your memory
return 0;
}
请注意,您还忘记释放内存,从而导致内存泄漏。要么使用 smart pointer,要么释放你的记忆,做一个好公民。
编辑:OP 编辑了他的问题以指定他还需要一个 dequeueAny
方法。我强烈建议您在回答问题后不要编辑您的问题(要求应尽可能静态)。无论如何,在那种情况下我建议你使用指针(或智能指针)而不是复制对象
class AnimalQueue {
std::list<Animal*> animals;
public:
void enqueue(Animal* a) {
animals.push_back(a); // Copy the pointer
}
Animal* dequeue() {
Animal *d = animals.front();
animals.pop_front(); // Destroy the pointer
return d;
}
};
int main() {
AnimalQueue q;
std::unique_ptr<Dog> d = std::make_unique<Dog>("Rex");
q.enqueue(d.get()); // This will now store the pointer to the object
// Try with dog-specific command
Animal *sameDog = q.dequeue(); // d.get() and sameDog are now pointing at the same object
if (d.get() == sameDog)
std::cout << "d.get() == sameDog" << std::endl;
std::cout << sameDog->name << std::endl;// Prints "Rex"
sameDog->speak();
return 0;
}