shared_ptr删除器类 - 为什么要复制删除器?
shared_ptr with deleter class - why deleter is copied?
假设我创建了一个带有自定义删除器的共享指针。在下面的代码中,我想检查删除器对象本身会发生什么:
struct A {
A() { std::cout << "An"; }
~A() { std::cout << "~An"; }
};
struct D {
D() {
std::cout << "Dn";
}
~D() {
std::cout << "~Dn";
}
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());
}
我看到"删除器"类的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
会发生什么?为什么需要复制这么多次?
我用 gcc 7.4 检查了你的代码,我得到了相同数量的析构函数调用。您观察到的是删除器对象在std::move(deleter)
中移动了六次。
由于已将析构函数添加到类中,因此将禁用默认移动语义的自动生成,并且需要显式定义它们:
D(D&&) = default;
D& operator=(D&&) = default;
但是,即使使用移动语义,析构函数仍可调用多达六次。
删除器通常是一个带有operator()
的空类。在优化的代码和编写良好的shared_ptr
实现中,空类的删除器不占用空间,因此复制开销为零。
实现通常知道在优化程序完成其工作后,围绕对象复制是否昂贵,以及是否必须采取预防措施。
在所介绍的案例中,我猜您正在观察一个未优化的构建。您会看到实现如何通过多层函数调用传递删除程序。在优化的版本中,实际上不会复制任何内容,因为删除器类D
为空。
创建自定义删除器很好奇,但另一方面,它缺乏优化,因为定义明确的共享指针将运行以实现删除功能。(+新(
如果你看一下shared_ptr在<memory>
的实现,你可以了解如何准备你的删除程序。
请注意,智能指针的实现因编译器和所使用的库而异。(例如,您的代码给了我 3 份副本(
仍在调查为什么会发生这种情况,但是是的,weak_ptr是更好的选择,因为它会破坏由shared_ptr管理的对象形成的参考循环。
相关文章:
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 为什么类中的ostringstream类型的成员会导致";调用隐含删除复制构造函数";错误
- std::ofstream 作为类成员删除复制构造函数?
- 删除复制构造函数的 Intel 13.1.2 中不良C++行为的解决方法
- c++17在编译时将带有已删除复制构造函数的类添加到std::vector
- 删除复制构造函数是否也会删除默认的复制/移动运算符?
- 用于删除复制/移动分配运算符的有效签名
- 为什么在具有引用字段的类中隐式删除复制运算符
- 为什么当类具有引用成员时C++编译器不删除复制构造函数
- 删除复制构造函数和运算符=类范围访问
- 是否可以使用 lambda 初始化变量(删除复制 ctor 时)
- 删除复制和复制转让 - 公共、私有还是受保护?
- 是否可以在基类中删除复制和移动构造函数/分配运算符
- 为什么具有已删除复制构造函数的结构不是 POD 类型
- 为什么删除复制构造函数会影响用户定义的默认构造函数?
- 带有向量的unique_ptr:错误:xxx的隐式删除复制构造函数
- 当提供用户定义的移动构造函数时,Visual Studio 2013不会删除复制构造函数
- 删除复制构造函数或复制赋值运算符算"user declared"吗?
- 为什么我们需要删除复制赋值运算符中的指针
- 卸载程序不会删除复制到VS2010安装程序项目中的系统文件夹。