如何使用父类类型的智能指针调用子类的析构函数

How to call the destructor of child class with a smart pointer of parent type?

本文关键字:调用 子类 析构函数 指针 智能 父类 类型 何使用      更新时间:2023-10-16

在我的应用程序中,我曾经有一个unique_ptr<parent> _member作为自定义类的成员。代码运行良好。然而,当我最近试图创建另一个从parent类继承的类,并在构造函数中使用_member = unique_ptr<child>(new child())初始化_member时,我意识到当自定义类被销毁时,_member的析构函数只调用父析构函数,但现在调用子析构函数。

这个行为对我来说是有意义的。因为毕竟_member的类型是unique_ptr<parent>。然而,我想知道我只给_member调用子析构函数的选项是什么。

行为没有意义。如果child的析构函数需要释放内存,而没有调用它,就会导致内存泄漏。

Parent需要有一个虚析构函数,以便在通过指向父类的指针删除派生类时调用派生类的析构函数。

经验法则:如果基类至少有一个虚函数,它应该有一个虚析构函数

除了尼尔的好答案,还有另一条路你可以走。

如果您使用std::shared_ptr而不是std::unique_ptr,您将获得std::shared_ptr的类型擦除特性的好处。让我们看一个例子:

#include <memory>
#include <iostream>
struct X {
    ~X() {
        std::cout << __PRETTY_FUNCTION__ << "n";
    }
};
struct Y : X {
    ~Y() {
        std::cout << __PRETTY_FUNCTION__ << "n";
    }
};
int main() {
    std::shared_ptr<X> obj = std::make_shared<Y>();
}

这段代码,运行时会输出:

Y::~Y()
X::~X()

我将使父类析构函数为虚函数。如果您有一个用例需要正确销毁没有虚函数的类,并且您不想使用std::shared_ptr的类型擦除销毁功能,则可以使用unique_ptr和自定义删除器实现相同的功能:

template <typename T>
void delete_function(void* ptr) {
    // Tell GCC that we know we're doing something stupid and
    // it need not warn us about it.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
    delete static_cast<T*>(ptr);
#pragma GCC diagnostic pop
}
template <typename T>
using erased_unique_ptr = std::unique_ptr<T, void(*)(void*)>;
template <typename T, typename... Args>
inline erased_unique_ptr<T>
make_unique_erased(Args&&... args) {
    return erased_unique_ptr<T>(new T(std::forward<Args>(args)...), &delete_function<T>);
}

观看Coliru现场直播