2

私は次のクラスを持っています:

class Socket
{
    Socket();
    Socket( SOCKET s );
};

class Connection : public virtual Socket
{
    Connection( IP ip );
};

これら 2 つのクラスには、いくつかの純粋仮想関数といくつかの非仮想関数、およびいくつかの独自のデータが含まれています。それらのポイントは、さまざまなプロトコルを実装して、いくつかのソケット タイプを派生させることです。

したがって、私は次の 2 つのクラスを専門としています。

class ProtocolSocket : public virtual Socket
{
    ProtocolSocket() {}
    ProtocolSocket( SOCKET s ) : Socket( s ) { ; }
};

class ProtocolConnection : public ProtocolSocket, public virtual Connection
{
    ProtocolConnection( SOCKET s, IP ip ) : ProtocolSocket( s ), Connection( ip ) {;}
};

何かがうまくいかない - あなたの多くが見ることができると確信しているように. ProtocolConnection を作成しようとしています:

new ProtocolConnection( s, ip );

構築は次のように進行します。

start ctor ProtocolConnection
    start ctor Connection
       start ctor Socket
          Socket(); - default ctor via Connection's init list
       end ctor Socket
       Connection(); - default ctor ProtocolConnection's init list
    end ctor Connection
    start ctor ProtocolSocket
       start ctor Socket     
          // Socket( s ); - skipped!!! - would have been from init 
          //                list of ProtocolSocket, but ctor for this object 
          //                already called!
       end ctor Socket
       ProtocolSocket( s ); -from init list of ProtocolConnection()
    end ctor ProtocolSocket
    ProtocolConnection( s, ip );
end ctor ProtocolConnection

その 2 番目の Socket コンストラクターをスキップすることは、言語仕様が行うべきことであり、正当な理由があります。

以前のものの代わりに、Socket( s ) が呼び出されたコンストラクターを呼び出すにはどうすればよいですか?

ProtocoSocket および ProtocolConnection オブジェクトと同じレベルで、OtherProtocolSocket および OtherProcolConnection などの複数の派生クラスを作成する予定です。

私が達成しようとしている効果は、ProtocolSocket および ProtocolConnection オブジェクトを構築し、それらを Socket および Connection オブジェクトとしてシステムに渡したいということです。したがって、特定のプロトコルを実装するソケットを作成した後は、基礎となるプロトコルの詳細を気にせずに、ソケットに読み書きするだけです。

Connection オブジェクトは、Socket オブジェクトからすべてのメソッドを継承する必要があります。

@アップデート:

DyP は、ProtocolConnection に Socket の初期化子を追加することを提案しています。これで問題は解決します。私はあなたにそれを受け入れたいと思います... しかし、それはただのコメントでした.

4

2 に答える 2

4

覚えておくべき重要な点は、仮想基底クラスのコンストラクターは、最も派生したクラスの初期化の一部として (および他の基底クラスが構築される前に) 実行されるということです。したがって、建設順序のスライドは正しくありません。

実際、ProtocolConnection を構築すると、最初に Socket が構築され、次に Connection (仮想的に継承したため) が構築され、最後に ProtcolSocket が構築されます。

必要なソケットのコンストラクターを呼び出すには、そのコンストラクターを ProtocolSocket メンバー初期化子リストの一部として呼び出す必要があります。

class ProtocolConnection: public ProtocolSocket, public virtual Connection
{
    public:
    ProtocolConnection(int s, int ip) :
        Socket(s), Connection(ip), ProtocolSocket(s)  
        // Note, also reordered, since all virtual bases are initialized before the
        // non-virtual bases
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

最後に、継承階層を単純化することをお勧めします。特に、仮想継承と複数のコンストラクターの使用は、要因を複雑にします。

于 2013-06-05T17:01:53.963 に答える
2

継承 DAG:

       ProtocolConnection
           /        \
     non-virtual  virtual
         /            \
ProtocolSocket     Connection
       |               |
    virtual         virtual
       |               |
    Socket           Socket

仮想継承によりSocket、 type のオブジェクトにはサブオブジェクトが1 つしかないことに注意してください。ProtocolConnection

[class.base.init]/10

まず、最も派生したクラス (1.8) のコンストラクターの場合のみ、仮想基底クラスは、基底クラスの有向非巡回グラフの深さ優先の左から右への走査に現れる順序で初期化されます。 to-right」は、派生クラス base-specifier-list 内の基本クラスの出現順序です。

仮想基本クラスの初期化は、深さ優先の左から右へのトラバーサルによって行われます。走査順序:

       (0) ProtocolConnection
             /             \
           nv               v
           /                 \
(1) ProtocolSocket    (3) Connection
         |                   |
         v                   nv
         |                   |
    (2) Socket         (4) Socket

次の初期化順序につながります。

(2); (3); (1); (0)
Socket; Connection; ProtocolSocket(非仮想基本クラス);ProtocolConnection

最も派生したクラスには、すべての仮想基本クラスProtocolConnectionの初期化子を含める必要があります。仮想基本クラスが最も派生したクラスの mem-initializer-list に表示されない場合、この仮想基本クラスのサブオブジェクトはデフォルトで構築されます。

于 2013-06-05T17:15:46.697 に答える