假设我有以下类和对象:
#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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,动物是一个抽象类。
如何正确删除指针animal,cat以及cow?
当我尝试时,delete animal;我收到以下警告消息:
警告:删除具有非虚拟析构函数的抽象类类型“动物”的对象将导致未定义的行为。
另一方面,当我尝试时,delete cat;我收到以下消息:
警告:删除具有非虚拟析构函数的多态类类型“Cat”的对象可能会导致未定义的行为。
一个基本的 C++ 规则说析构函数从派生类到基类工作。当 aCat被销毁时,则Cat先销毁部件,然后销毁Animal部件。
delete animal;是未定义的行为,因为为了正确遵循 C++ 销毁规则,必须在运行时知道应该在Animal基部分之前销毁哪个派生类部分。一个virtual析构函数正是这么做的-它能够动态分配机制,确保销毁工作的设计。
virtual但是,您没有析构函数,因此delete animal没有意义。无法调用正确的派生类析构函数,并且仅销毁Animal部分也不完全是有意义的行为。
因此,C++ 语言不假设在这种情况下会发生什么。
你的编译器很好地警告你这一点。
随着delete cat,情况略有不同。cat指针的静态类型是Cat*, 不是Animal*,因此即使没有任何动态调度机制,派生类析构函数首先调用的情况也很清楚。
编译器仍然会就此警告您,但它会使用不同的措辞(“可能导致”与“将导致”)。我相信原因是它Cat本身可能是更多派生类的基类,因为它已经是具有virtual函数的类层次结构的一部分。
显然,执行更完整的代码分析以找出delete cat真正无害的代码并不费心。
为了解决这个问题,制作Animal析构函数virtual。在此过程中,将原始指针替换为std::unique_ptr. virtual对于像您这样的类,您仍然必须遵循析构函数规则,但您不再需要执行手动delete.