次のコードを想定します。
class ChannelHandle;
class SessionHandle;
typedef ChannelHandle& ChannelHandleRef;
typedef SessionHandle& SessionHandleRef;
class RemoteChannelHandle
{
public:
RemoteChannelHandle(
ChannelHandleRef pChannelHandle, SessionHandleRef pSessionHandle);
RemoteChannelHandle(RemoteChannelHandle&&);
~RemoteChannelHandle();
void CloseChannel();
#ifndef _MSC_VER
RemoteChannelHandle& operator=(RemoteChannelHandle&&) = delete;
RemoteChannelHandle(RemoteChannelHandle const&) = delete;
RemoteChannelHandle& operator=(RemoteChannelHandle const&) = delete;
#endif
private:
LIBSSH2_CHANNEL* channel;
ChannelHandleRef channelHandle;
SessionHandleRef sessionHandle;
};
RemoteChannelHandle::RemoteChannelHandle(
ChannelHandleRef pChannelHandle, SessionHandleRef pSessionHandle)
: channel(nullptr), channelHandle(pChannelHandle), sessionHandle(pSessionHandle)
{
// OPEN SSH CHANNEL
}
RemoteChannelHandle::~RemoteChannelHandle()
{
try
{
CloseChannel();
}
catch (...)
{ }
}
void RemoteChannelHandle::CloseChannel()
{
if (channel == nullptr)
{
return;
}
// CLOSE SSH CHANNEL. THROW IF SOMETHING GOES WRONG
channel = nullptr;
}
- RemoteChannelHandle は SSH チャネルを開きますが、クリーンアップには 2 つの手順 (閉じる + 解放) が必要です。最初のステップは で実行され
~RemoteChannelHandle()
ますが、2 番目のステップは ChannelHandle の dtor で実行されます。したがって、 data memberchannelHandle
に注入する必要があるためchannel
です。この参照は、 の両方の手順を実行することで削除できます~RemoteChannelHandle()
。 sessionHandle
LIBSSH2_SESSION*
SSH チャネルを開くために必要なものを保持します。周りを回したくないのでLIBSSH2_SESSION*
、この参照を削除することはできません。
この問題は、RemoteChannelHandle の移動 ctor を定義すると発生します。「移動元」インスタンスのメンバーをクリアする必要があります。ただし、参照をクリアする方法はありません。それで、ここで何をしますか?
(const) std::shared_ptr
参照の代わりに使用しますか? 所有権がないため、ネイキッド ポインターを使用することもできます。参照をデータ メンバーとして使用することについては議論があることは承知していますが、move ctor を除けば、ハンドルを再配置する必要がある他のシナリオはないと予測しています (最初に参照を使用したのはそのためです)。- 参照をそのままにして、「状態」データメンバーを作成して、オブジェクトが有効な状態にあるかどうかを確認します(
channel != nullptr
この目的に使用できます)? - 他のアイデアはありますか?
これに代わるものを探しましたが、明確な答えは見つかりませんでした。もちろん、実際には明確な答えがないことを意味する可能性があります。
御時間ありがとうございます。
編集 (Mankarse への返信): ChannelHandle は、クリーンアップのステップ 2 を実行するためだけに存在します。簡単な定義は次のとおりです。
class ChannelHandle
{
public:
ChannelHandle();
ChannelHandle(ChannelHandle&&);
~ChannelHandle();
#ifndef _MSC_VER
ChannelHandle& operator=(ChannelHandle&&) = delete;
ChannelHandle(ChannelHandle const&) = delete;
ChannelHandle& operator=(ChannelHandle const&) = delete;
#endif
LIBSSH2_CHANNEL* GetChannel() { return channel; }
void SetChannel(LIBSSH2_CHANNEL* pChannel) { channel = pChannel; }
void FreeChannel();
private:
LIBSSH2_CHANNEL* channel;
};
SessionHandle は の RAII カプセル化ですLIBSSH2_SESSION*
。libssh2_session_init()
ctorとdtor を呼び出しlibssh2_session_free()
ます。他の同様のクラスは、SSH セッションの初期化/クリーンアップの他のステップを処理しますが、これはLIBSSH2_SESSION*
libssh2 から取得する場所であり、SessionHandle がそれを所有しています。繰り返しますが、単純化された定義は次のとおりです。
class SessionHandle
{
public:
SessionHandle();
SessionHandle(SessionHandle&&);
~SessionHandle();
void CloseSession();
LIBSSH2_SESSION* GetSession() { return session; }
#ifndef _MSC_VER
SessionHandle& operator=(SessionHandle&&) = delete;
SessionHandle(SessionHandle const&) = delete;
SessionHandle& operator=(SessionHandle const&) = delete;
#endif
private:
LIBSSH2_SESSION *session;
};