具有删除程序类的shared_ptr-为什么要复制删除程序?

时间:2019-12-18 03:39:52

标签: c++ shared-ptr copy-constructor

假设我使用自定义删除器创建了共享指针。在下面的代码中,我想检查一下删除对象本身会发生什么:

struct A {
    A() { std::cout << "A\n"; }
    ~A() { std::cout << "~A\n"; }
};

struct D {
    D() {
        std::cout << "D\n";
    }
    ~D() {
        std::cout << "~D\n";
    }
    D(const D&) {
        std::cout << "D(D&)\n";
    }
    void operator()(A* p) const {
        std::cout << "D(foo)\n";
        delete p;
    }
};

int main()
{
    std::shared_ptr<A> p(new A, D());
}

我发现,“ deleter”类的D(const D&)~D()又被调用了六次:

D
A
D(D&)
D(D&)
D(D&)
D(D&)
D(D&)
D(D&)
~D
~D
~D
~D
~D
~D
D(foo)
~A
~D

会发生什么?为什么需要复制很多次?

3 个答案:

答案 0 :(得分:0)

删除器通常是带有operator()的空类。在优化的代码和完善的shared_ptr实现中,空类的删除程序不占用空间,因此复制开销为零。

实现通常知道优化器完成其工作后在对象周围进行复制是否昂贵,以及是否必须采取预防措施。

在介绍的情况下,我想您正在观察未优化的构建。您将看到实现如何通过多层函数调用传递删除程序。在优化的版本中,删除器类D为空,因此实际上不会复制任何内容。

答案 1 :(得分:0)

创建自定义删除器很麻烦,但另一方面,它缺少优化,因为定义良好的共享指针将运行以实现删除功能。 (+新)

如果您在<memory>中查看shared_ptr的实现,则可以了解如何准备删除程序。

请注意,智能指针的实现根据所使用的编译器和库而有所不同。 (例如,您的代码给了我3份)

仍在调查为什么会发生这种情况,但是是的,weak_ptr是更好的选择,因为它打破了由shared_ptr管理的对象形成的参考周期。

答案 2 :(得分:0)

我使用gcc 7.4检查了您的代码,并且得到了相同数量的析构函数调用。您观察到的是删除器对象在std::move(deleter)中移动了六次。

在类中添加了析构函数后,默认移动语义的自动生成将被禁用,您需要明确定义它们:

D(D&&) = default;
D& operator=(D&&) = default;

但是,即使具有移动语义,析构函数仍可以被调用多达六次。