C++风格:可复制的句柄包装类

C++ Style: copyable handle wrapper classes

本文关键字:句柄 包装类 可复制 风格 C++      更新时间:2023-10-16

让封装共享句柄的类可复制是可以接受的C++风格吗?

我经常发现自己编写的类通过在类接口后面持有shared_ptr来隐藏一些粗糙的C库或操作系统资源的细节。例如

class window
{
public:
     window() : m_handle(OsCreateWindow(), OsDestroyWindow) {}
     void make_it_dance();
     void paint_it_red();
private:
     shared_ptr<OS_WINDOW_HANDLE> m_handle;
}

因为类是可复制的,并且shared_ptr做了艰苦的工作,所以实例可以随意传递,不会有任何泄漏或被破坏两次。所以,从技术上讲,一切都很好。我已经做了很多年了。

但最近我开始怀疑这种风格是否真的很好。毕竟,当类实例被复制时,句柄末尾的对象不会被复制。让所有这些类都不可复制,让用户清楚地知道他们正在处理对同一对象的引用,这会更好吗?现代C++强调"基于价值",而实例之间共享后端资源似乎违背了这一原则。

然而,结果是,我的代码中有很大一部分会处理指针,即使它们是智能指针。这似乎是一种倒退。

我想我理解你的困境。我有一个相当愚蠢的建议。

由于您的问题不是函数性的,而是期望复制您的window实例会创建,因此,如果将这样一个类命名为window_handle而不是window,不是更好吗?

这意味着它只是某个窗口的句柄,复制它不会创建新窗口或类似的东西,它只是复制一个句柄。

要强调的是,我建议你保留你的设计(对我来说这是一个很好的设计,似乎对你很有帮助),只需更改你的命名,就可以改变对代码更高层的期望。

就我个人而言,我不允许复制,并同意使用共享指针是一种倒退。我还要补充的是,每个"Window"实例都应该包含一个唯一的OS_Window_HANDLE。

您还可以使用MFC方法在对象之间传递句柄。MFC使用Attach和Detach方法,如下所示。。。

class Window
{
public:
  void Attach (OS_WINDOW_HANDLE handle)
  {
    ASSERT(NULL == m_handle);  // or you could destroy the existing handle
    m_handle = handle;
  }
  OS_WINDOW_HANDLE Detach()
  {
    OS_WINDOW_HANDLE retVal = m_handle;
    m_handle = NULL;
    return retVal;
  }
private:
  // disable copy constructor and assignment
  Window(const Window&);
  Window& operator=(const Window&);
};

如果句柄指向的资源不需要复制(浅拷贝),请使用std::shared_ptr。如果确实需要复制(深度复制),则应使用std::unique_ptr。如果不需要进行复制,请使用std::unique_ptr

一个可能的答案(以及引发问题的情况)是,静默地共享支持对象会使类的用户很难确保对象在特定点"死"。例如,对象可能是网络连接,并且用户出于安全原因需要知道该连接已经关闭。

如果连接包装器是可复制的,并且在实例之间共享连接,那么用户必须研究实例可能流动的路径,以确保副本不会保存在意外的地方,并保持连接打开。

另一方面,如果包装器是不可复制的,那么只有一个实例可以确保是死的。引用仍然可以分发,但一旦原始包装器死了,用户就可以确定支持对象也死了。

如果用户希望拥有共享副本,他们总是可以再次使用shared_ptr恢复该副本。但这一次是他们的决定。