unique_ptr<>和shared_ptr<>在销毁策略方面的区别

17

我一直在自学C++0x的智能指针,发现了一些让我感到不一致的地方。具体来说,unique_ptr<>和shared_ptr<>的销毁策略是如何处理的。

对于unique_ptr<>,您可以专门为std::default_delete<>进行特化,从此除非您明确请求不同的销毁策略,否则将使用新的默认值。

请考虑以下内容:

struct some_c_type;

some_c_type *construct_some_c_type();
void destruct_some_c_type(some_c_type *);

namespace std  {
    template <> struct default_delete<some_c_type> {
        void operator()(some_c_type *ptr) {
            destruct_some_c_type(ptr);
        }
     };
}

现在,一旦这个部分就位,unique_ptr<>将默认使用适当的销毁策略:

// Because of the specialization, this will use destruct_some_c_type
std::unique_ptr<some_c_type> var(construct_some_c_type());

现在将此与shared_ptr<>进行比较。对于shared_ptr<>,您需要显式请求适当的销毁策略,否则它将默认使用operator delete:

// error, will use operator delete 
std::shared_ptr<some_c_type> var(construct_some_c_type());

// correct, must explicitly request the destruction policy
std::shared_ptr<some_c_type> var(construct_some_c_type(),
                                 std::default_delete<some_c_type>());

两个问题。

  1. 我是否正确,每次使用shared_ptr<>都需要指定销毁策略,还是我漏掉了什么?
  2. 如果我没有漏掉什么,有什么想法可以解释为什么这两者不同?

P.S. 我关心这个问题的原因是我们公司进行大量混合C和C++编程。C++代码经常需要使用C风格的对象,因此指定不同的默认销毁策略的便利性对我来说非常重要。


2
这应该是 std::default_delete<some_c_type>() 吗?另外,顺便说一下,除非在 C++0x 中已经修复了,否则这三个都是棘手的。 - GManNickG
1
@RSamuel:我指的是令人烦恼的解析;在C++03中,它们都是函数声明。 - GManNickG
如果没有人回答的话,顺便说一句,我建议你在comp.lang.c++.moderated上发布这个问题,我相信那里肯定有人知道。如果你愿意的话,我可以帮你做,我对这个很感兴趣。 - GManNickG
@RSam:好的,我会给大家24小时时间在这里回答它,然后再发布到那里。 - GManNickG
1
@GMan:在这种情况下,这实际上更像是一个comp.std.c++的问题。这两者都不是当前定义的C++的一部分,至少对我来说,真正的问题是:“这个shared_ptr之间的差异是毫无意义还是有意义的?”现在,它看起来可能是毫无意义的,但是... - Jerry Coffin
显示剩余7条评论
1个回答

4
我认为问题在于为什么std::shared_ptr可以没有关联的删除器(在这种情况下,它只调用delete),而不是默认构造一个std::default_delete。 (我不知道。如果意图是让default_delete用于特化,人们会希望它被shared_ptr使用。)
否则会存在权衡。
最好少使用模板参数。 Boost的参考提到,这允许工厂更改分配方案,而不会影响工厂的使用者。
另一方面,unique_ptr应该非常轻量级。如果删除器不是类型的一部分(对于没有成员的函数对象),如何以零空间开销存储它? (GCC使用tuple,在那里无成员对象不占用内存空间 )
主观而言,我认为我更喜欢:
unique_ptr<FILE, FCloser> f(fopen(x, y));

为了

unique_ptr<FILE> f(fopen(x, y)); //default_delete<FILE> has been specialized

在第一种情况下,没有什么需要猜测的。如果资源不来自newnew[],则必须明确给出删除器。

1
我同意:显式优于隐式。 - dalle
@UncleBens - 隐式/显式讨论的一个问题是标准特别允许使用隐式,这将始终编译但可能导致未定义的行为。如果您总是必须为简单的deletedelete []指定删除器,那可能没关系。但事实上,很容易忘记,构建将成功,这样的错误可能很容易被忽略。 - R Samuel Klatchko
1
为FILE等专门化static_assert? :) 但即使您进行了专门化,仍需要记得包含具有专门化的头文件? - UncleBens
对于像FILE这样的类型,您需要包含头文件。但是对于本地C类型,我们可以在声明C类型的构造函数/析构函数的同一文件中包含专门化。 - R Samuel Klatchko
这个语法的问题在于它会让与 C api 交互之类的事情变得丑陋。我想能够做类似于 unique_ptr<THandle> ptr(LIB_T_Make_a_T(params....), LIB_T_Destroy_a_T); 这样的事情,其中 LIB_T_Destroy_a_T 的类型是 void(THandle*)。 - Flame
哦,我不想用自己的模板特化来污染std::。 - Flame

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接