アイテムを所有するセッションを制御するライブラリへのCAPIを考えると、CAPIをRAIIC ++クラスにカプセル化するための最良の設計は何ですか?
CAPIは次のようになります。
HANDLE OpenSession(STRING sessionID);
void CloseSession(HANDLE hSession);
HANDLE OpenItem(HANDLE hSession, STRING itemID);
void CloseItem(HANDLE hItem);
さらに、これらのタイプ(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); }
};
この設計には、悪い動作を許可するという欠点があります。すべてのItemオブジェクトが破棄される前に、Sessionオブジェクトが破棄される(およびCloseSession関数が呼び出される)可能性があります。それが起こるべきではないので、これは迷惑です。C APIを使用すると、この誤った動作が発生する可能性があり、したがって有効ではない場合でも、C++APIの設計によって回避されるようにしたいと思います。
そのため、セッションにアイテムが含まれ(これは実際の関係を示しています)、アイテムを作成および破棄できる唯一のクラスである次のデザインを使用することを考えています。
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;
}
}
};
アイテムが閉じられる前にセッションが閉じられないようにする唯一の方法は、アイテムオブジェクトがセッションのメンバーであるため、セッションが破棄される前に破棄されることを設計に反映しているように見えます。
ただし、Sessionクラスのインターフェイスにこれらの関数OpenItemとCloseItemが残っているため、私には少し奇妙に見えます。私はRAIIのラインでもっと何かを探していました(私にとって、これはItemのコンストラクターを使用することを意味します)が、正しい破棄順序を保証するカプセル化の方法を想像することはできません。
さらに、ポインタを使用すると、newとdeleteは古すぎるC++です。クラスItemの移動セマンティクスを正しく定義するという代償を払って(Item *の代わりに)Itemのベクトルを使用できるはずですが、初期化されていない2番目のクラスを作成するItemのデフォルトコンストラクターを許可するという代償があります。市民アイテムオブジェクト。
より良いデザインのアイデアはありますか?