如何将C API封装到RAII C++类中?

Did*_*set 7 c++ api encapsulation raii

给定一个控制具有项目的会话的库的C API,将C API封装到RAII C++类中的最佳设计是什么?

C API看起来像:

HANDLE OpenSession(STRING sessionID);
void CloseSession(HANDLE hSession);
HANDLE OpenItem(HANDLE hSession, STRING itemID);
void CloseItem(HANDLE hItem);
Run Code Online (Sandbox Code Playgroud)

加上对这些类型之一(Session或Item)有用的其他函数,并直接映射到相关对象的C++成员函数.但这里不需要它们.我主要关注的是构建和销毁这些对象,使用RAII来管理这些类的正确打开和关闭.

我设计课程的第一个想法是纯粹而直接的RAII.包含的类接受容器对象作为构造函数参数.

class Session {
    HANDLE const m_hSession;
public:
    Session(STRING sessionID): m_hSession(OpenSession(sessionID)) {}
    ~Session() { CloseSession(m_hSession); }
};
class Item {
    HANDLE const m_hItem;
public:
    Item(HANDLE hSession, STRING itemID): m_hItem(OpenItem(hSession, itemID)) {}
    ~Item() { CloseItem(m_hItem); }
};
Run Code Online (Sandbox Code Playgroud)

这种设计的缺点是允许不良行为:在破坏所有Item对象之前,可以破坏Session对象(并调用CloseSession函数).这很烦人,因为它不应该发生.即使这种错误行为是可能的,因此无效,使用C API,我希望通过C++ API中的设计来避免它.

这就是为什么我想知道使用以下设计,其中Session包含其Items(这显示了实际的关系),并且是唯一能够构造和销毁Items的类.

class Item {
    HANDLE const m_hItem;
    Item(HANDLE hSession, STRING itemID): m_hItem(OpenItem(hSession, itemID) {}
    ~Item() { CloseItem(m_hItem); }
    friend class Session;
public:
};
class Session {
    HANDLE const m_hSession;
    typedef vector<Item *> VecItem;
    VecItem m_vecItem;
    Session(STRING sessionID): m_hSession(OpenSession(sessionID)) {}
    ~Session() {
        for (size_t n = 0 ; n < m_vecItem.size() ; ++n) delete m_vecItem[n];
        m_vecItem.clear();
        CloseSession(m_hSession);
        }
public:
    Item * OpenItem(STRING itemID) {
        Item *p = new Item(m_hSession, itemID);
        m_vecItem.push_back(p);
        return p;
        }
    void CloseItem(Item * item) {
        VecItem::iterator it = find(m_vecItem.begin(), m_vecItem.end(), item);
        if (it != m_vecItem.end()) {
            Item *p = *it; m_vecItem.erase(it); delete p;
            }
        }
};
Run Code Online (Sandbox Code Playgroud)

它在我看来是确保Session关闭之前没有关闭的唯一方法:在设计中反映Item对象是Session的成员,因此在Session被销毁之前将被破坏.

但是,它看起来有点奇怪,因为它在Session类的接口中留下了OpenItem和CloseItem这些函数.我正在寻找RAII系列中的更多内容(对我而言,这意味着使用Item的构造函数),但无法设想一种封装它的方法,以确保正确的销毁顺序.

此外,使用指针,new和delete是太旧的世纪C++.应该可以使用Item的向量(而不是Item*),代价是正确定义类Item的移动语义,但它的代价是允许Item的默认构造函数创建未初始化的第二类公民物品对象.

有更好的设计理念吗?

Mic*_*son 5

通过添加另一层(并使您的RAII更加明确),您可以获得非常整洁的东西.Sessions和Items的默认复制构造函数和赋值是正确的.所有项目的HANDLE关闭后,会话的HANDLE将关闭.没有必要保留儿童的矢量,共享指针跟踪所有这些...所以我认为它应该做你需要的一切.

class SessionHandle
{
   explicit SessionHandle( HANDLE in_h ) : h(in_h) {}
   HANDLE h;
   ~SessionHandle() { if(h) CloseSession(h); }
};

class ItemHandle
{
   explicit ItemHandle( HANDLE in_h ) : h(in_h) {}
   HANDLE h;
   ~ItemHandle() { if(h) CloseItem(h); }
};

class Session
{
   explicit Session( STRING sessionID ) : session_handle( OpenSession(sessionID) )
   {
   }
   shared_ptr<SessionHandle> session_handle;
};

class Item
{
   Item( Session & s, STRING itemID ) : 
     item_handle( OpenItem(s.session_handle.get(), itemID ) ), 
     session_handle( s.session_handle )
   {
   }
   shared_ptr<ItemHandle> item_handle;
   shared_ptr<SessionHandle> session_handle;
};
Run Code Online (Sandbox Code Playgroud)