如何将删除器传递给 make_shared?

How to pass deleter to make_shared?

自 C++11 以来,由于多种原因,开发人员倾向于将智能指针 classes 用于动态生命周期对象。对于那些新的智能指针 classes,标准,甚至建议不要使用像 new 这样的运算符,而是建议使用 make_sharedmake_unique 来避免一些容易出错的操作。

如果我们喜欢使用智能指针class,比如shared_ptr,我们可以构造一个,比如

shared_ptr<int> p(new int(12));

我们还想将自定义删除器传递给智能指针 classes,

shared_ptr<int> p(new int(12), deleter);

另一方面,如果我们喜欢使用 make_shared 来分配,例如。 int,而不是使用 newshared_ptr 构造函数,就像上面的第一个表达式一样,我们可以使用

auto ip = make_shared<int>(12);

但是如果我们还想将自定义删除器传递给 make_shared,有什么正确的方法吗?似乎编译器,至少是 gcc,给出了一个错误,

auto ip = make_shared<int>(12, deleter);

你不能。 make_shared<T> 将提供的参数转发给 T 类型的构造函数。当您需要默认删除器时,它用于简单的情况。

documentation 开始,make_shared 接受一个 参数列表,用于构造 T 的实例
此外,文档说:

This function is typically used to replace the construction std::shared_ptr(new T(args...)) of a shared pointer from the raw pointer returned by a call to new.

因此,您可以推断出您无法设置自定义 删除器
为此,您必须通过右 constructor.
为自己创建 shared_ptr 作为建议列表中构造函数的示例,您可以使用:

template< class Y, class Deleter > 
shared_ptr( Y* ptr, Deleter d );

因此,代码将类似于:

auto ptr = std::shared_ptr(new MyClass{arg1, arg2}, myDeleter);

而不是:

auto ptr = std::make_shared<MyClass>(arg1, arg2);

正如其他人所说,make_shared 不能与自定义删除器一起使用。但我想解释一下为什么。

自定义删除器的存在是因为您以某种特殊方式分配了指针,因此您需要能够以相应的特殊方式释放它。好吧,make_shared 分配指针与 new。用 new 分配的对象应该用 delete 释放。标准删除器尽职尽责。

简而言之,如果您可以接受默认的分配行为,那么您也可以接受默认的 解除分配 行为。如果您不能接受默认的分配行为,您应该使用 allocate_shared,它使用提供的分配器来分配和释放存储。

此外,make_shared 被允许(并且几乎肯定会)在同一个分配中为 T 分配内存和为 shared_ptr 分配控制块。这是您的删除者无法真正了解或处理的事情。而 allocate_shared 能够处理它,因为您提供的分配器可以执行分配和释放任务。

未指定 make_shared 如何获取对象的内存(它可以使用 operator newmalloc 或某种分配器)因此自定义删除器无法知道如何做正确的事。 make_shared 创建对象,因此您还必须依靠它来正确销毁对象并进行适当的清理,无论是什么。

Also we would like to pass a custom deleter to smart pointer classes,

shared_ptr<int> p(new int(12), deleter);

我认为这不是一个很现实的例子。当以某种特殊方式获取资源时,通常会使用自定义删除器。如果您只是像这样使用 new 创建它,那么为什么您仍然需要自定义删除器?

如果您只想让一些代码在销毁时 运行 就把它放在析构函数中!这样您仍然可以将它与 make_shared 一起使用,例如

struct RunSomethingOnDestruction {
  RunSomethingOnDestruction(int n) : i(n) { }
  ~RunSomethingOnDestruction() { /* something */ }
  int i;
};

auto px = std::make_shared<RunSomethingOnDestruction>(12);
std:shared_ptr<int> p(px, px->i);

这为您提供了一个由 make_shared 创建的 shared_ptr<int>(因此您可以通过 make_shared 完成内存优化),它将 运行 一些自定义代码销毁.

如果您使用自定义删除器,则在创建智能指针对象时不能使用 make_uniquemake_shared 函数。由于我们需要提供自定义删除器,因此这些功能不支持。

如果您需要自定义删除器或采用来自其他地方的原始指针,请不要使用 make_unique 或 make_shared

我们的想法是,如果您需要一种专门的方法来删除您的对象,您可能也需要一种专门的方法来创建它们。

假设我们class测试

#include <iostream>    
using namespace std;    
class Test
{
private : 
    int data; 
    public : 
    Test() :data{0}
    {
        cout << "Test constructor (" << data << ")" << endl;
    }
    Test(int d) : data{ d }
    {
        cout << "Test constructor (" << data << ")" << endl; 
    }
    int get_data() const { return data; }
    ~Test()
    { 
        cout << "Test Destructor (" << data << ')' << endl; 
    }
};

// main function. 
int main()
{
   // It's fine if you use  make_shared and custom deleter like this
   std::shared_ptr<Test> ptr(new Test{1000},
            [](Test *ptr)
            {
                cout << "some Code that you want to execute "; 
                delete ptr;
            });
         return 0;
}

但是如果你使用make_shared函数你会得到一个编译错误

std::shared_ptr<Test> ptr = make_shared<Test>(1000,
            [](Test *ptr){
               cout << "some Code that you want to execute "; 
               delete ptr;
            });

基本上 make_shared 函数是 newdelete 的包装器,如果您想要自定义删除器,您必须提供自己的 newdelete