2 つのクライアントが ZeroMQ PUB/SUB ソケットを使用して通信できるようにするライブラリを作成しています。各クライアント アプリケーションは、ブロードキャスター エンドポイントまたはレシーバー エンドポイントのいずれかをインスタンス化し、これらのエンドポイント クラスには Connection メンバーがあります。
class Connection {
Connection(const char* address, int outgoingPort, int incomingPort);
};
接続にはいくつかのソケットがあり、それぞれのポートを介して指定されたアドレスに接続するように構成されています。ただし、接続が実際にインスタンス化されるクラスでこれらの詳細を公開することは気にしません。ベース接続オブジェクトには着信ポートと発信ポートがありますが、この詳細はプログラムの残りの部分に浸透する必要はありません。これらの上位層では、データ ポートと制御ポートという 2 つの指定ポートの観点から考える方が賢明です。そのため、特定のタイプの接続の着信ポートと発信ポートを定義するコンストラクターを実装する 2 つのサブクラスがあります。
class BroadcasterConnection : public Connection {
BroadcasterConnection(int dataPort, int controlPort)
:Connection("*", dataPort, controlPort) {}
};
class ReceiverConnection : public Connection {
ReceiverConnection(const char* hostAddress, int dataPort, int controlPort)
:Connection(hostAddress, controlPort, dataPort) {}
};
"*"
さらに、ブロードキャスターは安定したエンドポイントとしてポートにバインドするため、実際のリモート アドレスの代わりに使用する必要があります。繰り返しになりますが、ブロードキャスター接続をインスタンス化して使用するクラスは、この詳細に関与する必要がないため、BroadcasterConnection コンストラクターが処理します。
別の例として、ZeroMQ ソケットをラップするクラスで同じことを行います。ベース Socket クラスがあり、サブクラス コンストラクターは適切な値 (ZMQ_PUB または ZMQ_SUB) を ZeroMQ ヘッダーから基になるソケットに渡すだけです。クライアントが ZeroMQ の値を直接使用することはできないため、正式な方法で PUB ソケットと SUB ソケットの区別を成文化する必要があり、単一のサブクラス コンストラクターを提供することは、透過的で賢明な方法のように思えました。
class Socket:
Socket(void* context, const char* address, int port, int socketType);
class PublishSocket : public Socket:
PublishSocket(void* context, const char* address, int port)
:Socket(context, address, port, ZMQ_PUB) {}
class SubscribeSocket : public Socket:
SubscribeSocket(void* context, const char* address, int port)
:Socket(context, address, port, ZMQ_SUB) {}
これらのサブクラスは、特別なことは何もしませんが、抽象化のサービスにおいて有用で健全な追加であることに同意していただければ幸いです。しかし、私はこの単純な慣用句の一般的な名前を知りません。コンストラクターのみを実装するサブクラスを定義するとき、より特殊化されたパラメーターのセットを使用してオブジェクトを構築するためだけに、私は何をしていますか?
ここで重要な点は、これらのサブクラスが追加のメソッドやデータを定義していないということです。別の例を次に示します。ここには、任意のタイプの任意のエンティティを識別する基本 Tag クラスがあります。サブクラスは、いくつかのドメイン固有のパラメーターに基づいてエンティティの個々のタイプのタグを作成するために使用されますが、最終的にはすべて Tag オブジェクトになります。
Tag(char typeIdentifier, int entityIdentifier);
LightTag(int lightIndex):Tag('L', lightIndex) {}
SkeletonTag(const char* skeletonName):Tag('S', hash(skeletonName)) {}
CameraTag():Tag('C', 0) {}
それで、いくつか質問があります:
このイディオムに一般的に使用されている Google 対応の名前はありますか?
と書く
Connection c = BroadcasterConnection(40001, 40002);
と、コピーコンストラクターが呼び出されます。は追加のデータを定義していないためBroadcasterConnection
、2 つのクラスは互換性があり (RTTI に関係なく)、オブジェクトのスライスを心配することなくダウンキャストできるはずですよね? コピーを回避するこの方法でオブジェクトを構築するための同様に便利な構文はありますか? これは、コンストラクターの初期化リストでも発生するようです。Connection* c = new BroadcasterConnection(40001, 40002);
これはあまり実用的な例ではありませんが、次にと書いたとしdelete c;
ます。Connection には仮想デストラクタがありませんが、そもそも仮想関数がありません (したがって、vtable はありません)。BroadcasterConnection は、追加データを定義しない Connection の直接のサブクラスであるため、この操作は安全でしょうか? BroadcasterConnection がメンバー データを追加した場合はどうなるでしょうか。その後、メモリリークが発生しますか?特定のサブクラスが上記の方法でコンストラクターのみであるという事実を明示的に体系化して、コンパイラーが追加のデータを含めることを許可しないようにする方法はありますか?
そしてもちろん、同じ問題を根本的に解決するためのより良い方法があれば、ぜひ聞きたいです。