使用抽象类时如何正确删除指针

Kel*_*inS 7 c++

假设我有以下类和对象:

#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)

请注意,动物是一个抽象类。

如何正确删除指针animalcat以及cow

当我尝试时,delete animal;我收到以下警告消息:

警告:删除具有非虚拟析构函数的抽象类类型“动物”的对象将导致未定义的行为。

另一方面,当我尝试时,delete cat;我收到以下消息:

警告:删除具有非虚拟析构函数的多态类类型“Cat”的对象可能会导致未定义的行为。

Chr*_*ckl 8

一个基本的 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.