一旦引用计数达到0,就让shared_ptr调用成员函数

时间:2010-06-20 11:48:20

标签: c++ shared-ptr raii

我正在为一个不能与DuplicateHandle一起使用的HANDLE创建一个包装器,所以我试图将句柄包装在shared_ptr中。

想象一下以下代码:

class CWrapper
{
public:
    CWrapper() :
        m_pHandle(new HANDLE, &CWrapper::Close)
    {
        //code to open handle
    }

private:
    void Close() 
    { 
        //code to close handle
    }

    std::shared_ptr<HANDLE> m_pHandle;
}

我也尝试使用HANDLE参数创建close(不理想)。无论哪种方式,我得到编译器错误“术语不评估为采用0参数的函数”。这是因为隐含的这个指针吗?我该如何解决?如何从共享指针调用成员函数?

5 个答案:

答案 0 :(得分:6)

你不能调用成员函数,但你可以使用像这样的全局函数的自定义删除器:

void my_deleter(Foo *ptr)
{
 delete ptr;
 std::cout<< "using custom deleter" <<std::endl;
}
shared_ptr<Foo> pf (new Foo, my_deleter); 

答案 1 :(得分:5)

我认为你的抽象方式错误。

shared_ptr为您提供了一个不可复制的共享资源的可复制“句柄”。使用shared_ptr的类型在删除时不执行自己的清理不是最佳用途。

如果让您的班级负责在其析构函数中正确清理这种固有的不可复制资源,那么您可以使用shared_ptr来提供共享所有权,这是它应该承担的唯一责任。 (我认为HANDLE是不可复制的,就好像你试图制作HANDLE的简单副本一样,副本不能被视为独立;最后一个副本必须正确关闭,因此副本的所有者会需要知道现有的其他副本。)

class CWrapper
{
public:
    CWrapper()
    {
        // code to open handle
    }

    ~CWrapper()
    {
        // code to close handle
    }

private:
    // prevent copying
    CWrapper(const CWrapper&);
    CWrapper& operator=(const CWrapper&);

    HANDLE mHandle;
};

现在使用shared_ptr<CWrapper>您需要共享句柄的地方,如果您认为这样过于冗长,可以使用typedef。

自定义删除器是一个过于复杂的解决方案,恕我直言。

答案 2 :(得分:5)

如果您需要从Close内访问非静态成员,则需要正确绑定其this参数

CWrapper() :
   m_pHandle(new HANDLE, boost::bind(&CWrapper::Close, this, _1)) {
    //code to open handle
}

然而,这包含隐藏的错误。您的对象是可复制的,并将删除器绑定到*this的对象。句柄与您创建的第一个包装器相关联,但是如果您复制包装器,则句柄是共享的,但与第一个包装器相关联,这可能不再存在:

CWrapper getWrapper() { CWrapper w; return w;  }
CWrapper x = getWrapper();

执行该代码并且x将被销毁后,行为未定义,因为x对内部句柄指针的破坏将尝试使用w中绑定的对象的构造函数invokation - 然而该对象不再存在!

对此的解决方案是将与句柄关联的数据存储在已分配的对象本身中,而不是尝试将其存储在顶层句柄对象中,如下面的代码所示

class CWrapper
{
public:
  CWrapper():m_pHandle(new CHandle)
  { }

private:
    // This class cannot be copied
    class CHandle : boost::noncopyable {
      friend class CWrapper;

      CHandle() 
        :m_pHandle(new HANDLE) {
          // code to open handle
      }

      ~CHandle() {
        // code to close this handle, making use of 
        // auxilary data for whatever reason
      }

    private:
      boost::scoped_ptr<HANDLE> m_pHandle;
      // auxilary data associated with the handle...
    };

    boost::shared_ptr<CHandle> m_pHandle;
};

辅助数据不再存储在句柄中,而是与包装器的所有副本之间共享的数据一起存储。共享数据本身是使用其常规构造函数和析构函数创建和销毁的。

CWrapper getHandle() { return myHandle; }
CWrapper w = getHandle();

生命中的最后一个包装器将破坏句柄,该句柄在所有包装器之间显式共享。

答案 3 :(得分:3)

我没有对它进行过测试,但根据shoosh提出的想法,您可以通过这样的成员函数:

void Class::myDeleteMember(Foo *ptr)
{
 delete ptr;
 std::cout<< "using custom deleter" <<std::endl;
}
shared_ptr<Foo> pf (new Foo, boost::bind(&Class::myDeleteMember, _1)); 

答案 4 :(得分:0)

在boost :: shared_ptr docs中查找deleter。我找不到它的直接链接,但基本上它是一个在ref为0时调用的仿函数。