假设我有以下类和对象:
#include <iostream>
class Animal
{
public:
virtual void makeNoise() = 0;
void eat()
{
std::cout << "Eating..." << "\n";
}
void sleep()
{
std::cout << "Sleeping..." << "\n";
}
};
class Cat: public Animal
{
public:
void makeNoise()
{
std::cout << "Miow..." << "\n";
}
};
class Cow: public Animal
{
public:
void makeNoise()
{
std::cout << "Mooo..." << "\n";
}
};
int main()
{
Animal *animal;
Cat *cat = new Cat();
Cow *cow = new Cow();
animal = cat;
animal->eat();
animal->sleep();
animal->makeNoise();
animal = cow;
animal->eat();
animal->sleep();
animal->makeNoise();
return 0;
}
请注意,动物是一个抽象类。
如何正确删除指针animal
,cat
和cow
?
当我尝试delete animal;
时,我收到以下警告消息:
警告:删除抽象类类型对象&#39; Animal&#39;其中有 非虚析构函数会导致未定义的行为。
另一方面,当我尝试delete cat;
时,我收到以下消息:
警告:删除多态类类型的对象&#39; Cat&#39;其中有 非虚析构函数可能会导致未定义的行为。
答案 0 :(得分:2)
一个基本的C ++规则说析构函数从派生类到基类的工作方式。当Cat
被销毁时,Cat
部分首先被销毁,Animal
部分被销毁。
delete animal;
是未定义的行为,因为为了正确遵循C ++销毁规则,必须在运行时知道哪个派生类部分应该在Animal
基础部分之前销毁。一个virtual
析构函数正是这样做的 - 它启用了一个动态调度机制,确保销毁按设计工作。
但是,您没有virtual
析构函数,因此delete animal
只是没有意义。没有办法调用正确的派生类析构函数,并且只销毁Animal
部分也不是完全有意义的行为。
因此,C ++语言不会假设在这种情况下会发生什么。
你的编译器非常适合警告你。
delete cat
,情况略有不同。 cat
指针的静态类型是Cat*
,而不是Animal*
,所以即使没有任何派生类析构函数首先调用的动态调度机制,它也很清楚。
编译器仍然会对此发出警告,但它会使用不同的措辞(&#34;可能导致&#34; vs.&#34;将导致&#34;)。我相信原因是Cat
本身可能是更多派生类的基类,因为它已经是具有virtual
函数的类层次结构的一部分。
显然没有必要执行更完整的代码分析,以发现delete cat
真的无害。
要解决此问题,请创建Animal
析构函数virtual
。在您使用std::unique_ptr
时,请将原始指针替换为virtual
。对于像您这样的类,您仍然必须遵循delete
析构函数规则,但您不再需要执行手册Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET'], scope: '', info_fields: ''
end
。
答案 1 :(得分:0)
这正是编译器警告你的内容。您需要为每个类定义和实现自己的析构函数,并确保将它们定义为virtual
以进行继承。