24

在 C++11 中是否可以用来shared_ptr控制非指针资源?


可以unique_ptr用来管理非指针资源。这是通过实现一个自定义删除器类来完成的,该类提供:

  1. A typedef {TYPE} pointer;where{TYPE}是非指针资源类型
  2. operator()(pointer)释放受控资源

...然后unique_ptr使用自定义删除器实例化 a 作为第二个模板参数。

例如,在 Windows 下,可以创建一个unique_ptr管理服务控制句柄。这种句柄类型不是通过调用释放的delete,而是通过调用释放的CloseServiceHandle()。这是执行此操作的示例代码:

自定义删除器

struct SvcHandleDeleter
{
    typedef SC_HANDLE pointer;
    SvcHandleDeleter() {};

    template<class Other> SvcHandleDeleter(const Other&) {};

    void operator()(pointer h) const
    {
        CloseServiceHandle(h);
    }
};


typedef std::unique_ptr<SC_HANDLE,SvcHandleDeleter> unique_sch;

实例化

unique_sch scm(::OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS));

是否也可以用来shared_ptr控制非指针资源?

根据文档,shared_ptr构造函数重载提供了提供自定义删除器类的方法,但是没有一个构造函数接受既不是指针也不是指针包装器的资源类型。

如何才能做到这一点?

4

4 回答 4

15

可悲的是,shared_ptr对类型擦除的需求使得当前接口无法准确实现您想要的。unique_ptr设法做到这一点,因为它有关于实际删除器类型的静态信息,它可以从中绘制实际的“指针”类型。在shared_ptr的情况下,删除器类型在类型擦除过程中丢失(这就是您不能在shared_ptr模板中指定它的原因)。

另请注意,unique_ptr它不提供任何转换构造函数shared_ptr(例如template<class Y> shared_ptr(Y* p))。它不能这样做,因为pointer不一定是真正的指针类型,因此它不能限制可以接受的内容(除了可能通过一些 SFINAEstd::is_convertible_to或类似的东西......但我离题了)。

现在,一个明显的解决方法是简单地new处理资源句柄,这听起来很愚蠢。:/

std::shared_ptr<SC_HANDLE> sp(new SC_HANDLE(::OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS)),
    [](SC_HANDLE* p){ ::CloseServiceHandle(*p); delete p; });
于 2012-07-25T14:52:02.817 回答
5

好吧,一旦释放对指针的最后一个引用,shared_ptr 将调用指向对象的析构函数,然后可以释放类包含的任何内容。只是做一个类可能是这样的:

struct SvcHandle
{
  typedef SC_HANDLE pointer;
  SvcHandle()
  :M_handle(::OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS))
  { }

  ~SvcHandle()
  {
      CloseServiceHandle(M_handle);
  }
private:
  pointer M_handle;
};

然后只需使用新的 SvcHandle 创建一个共享指针。句柄的生命周期管理将与 shared_ptr - RAII 处于最佳状态。

于 2012-07-25T14:42:52.773 回答
1

这个怎么样?

auto scm = make_shared<unique_sch>(::OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS));

unique_sch是您在问题中提到的课程。现在使用 scm 作为资源的共享指针。在需要时取消引用并不是最好的事情,但你确实问过这是否可能。

但这仍然使用指针。从文档中可以看出,unique_ptr类将指针类作为构造函数参数,它实际上可以是任何东西。 然而shared_ptr将一个类型作为模板参数,并在其构造函数中强制将其转换为指向该类型的指针:

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

我认为可以肯定地说它不能直接用于管理非指针资源,因为shared_ptr类假设其资源是指针。

于 2012-07-25T14:52:53.130 回答
0

认为没有。由于标准为 shared_ptr 提供了这样的构造函数,而没有其他构造函数。

// 20.7.2.2.1, constructors:
constexpr shared_ptr() noexcept;
template<class Y> explicit shared_ptr(Y* p);
template<class Y, class D> shared_ptr(Y* p, D d);
template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
template <class D> shared_ptr(nullptr_t p, D d)
template <class D, class A> shared_ptr(nullptr_t p, D d, A a)
template<class Y> shared_ptr(const shared_ptr<Y>& r, T *p) noexcept;
shared_ptr(const shared_ptr& r) noexcept;
template<class Y> shared_ptr(const shared_ptr<Y>& r) noexcept;
shared_ptr(shared_ptr&& r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y>&& r) noexcept;
template<class Y> explicit shared_ptr(const weak_ptr<Y>& r);
template<class Y> shared_ptr(auto_ptr<Y>&& r);
template <class Y, class D> shared_ptr(unique_ptr<Y, D>&& r);
constexpr shared_ptr(nullptr_t) : shared_ptr() { }

例如,你想怎么做(对于 unique_ptr)

pointer release() noexcept;

1 后置条件:get() == nullptr。2 返回: get() 在调用 release 开始时的值。

没有指针资源?你试图破解语言。这总是坏主意。

于 2012-07-25T14:47:02.547 回答