12

これが理想的な継承階層です。

class Foobar;

class FoobarClient : Foobar;

class FoobarServer : Foobar;

class WindowsFoobar : Foobar;

class UnixFoobar : Foobar;

class WindowsFoobarClient : WindowsFoobar, FoobarClient;

class WindowsFoobarServer : WindowsFoobar, FoobarServer;

class UnixFoobarClient : UnixFoobar, FoobarClient;

class UnixFoobarServer : UnixFoobar, FoobarServer;

これは、継承階層がFoobar2 回から継承しようとするためであり、そのため、コンパイラは のメンバーに対するあいまいな参照について不平を言うからですFoobar

どうしてこんなに複雑なモデルが必要なのか説明させてください。これは、 、 、 、および から同じ変数にアクセスできるようにWindowsFoobarするUnixFoobarためFoobarClientですFoobarServer。これは問題にはなりませんが、任意のプラットフォームでサーバー/クライアント関数を使用し、クライアントまたはサーバーでプラットフォーム関数を使用できるように、上記の任意の組み合わせで多重継承を使用したいだけです。

これは多重継承のやや一般的な問題だと感じずにはいられません...私はこの問題に完全に間違った角度から取り組んでいますか?

更新 1:

また、#ifdefこれを回避するために使用できることを考慮してください。ただし、これは次のような非常に醜いコードを生成する傾向があります。

CFoobar::CFoobar()
#if SYSAPI_WIN32
: m_someData(1234)
#endif
{
}

...うん!

更新 2:

この問題の背景をもっと読みたい人は、該当するメーリング リストのスレッドに目を通すことを強くお勧めします。3回目の投稿あたりから面白くなり始めます。また、問題の実際のコードをここで確認できる関連コード コミットもあります。

4

8 に答える 8

18

基本クラスのコピーが 2 つ得られますが、これは機能します。Foobar単一のコピーを取得するには、仮想継承を使用する必要があります。ここで多重継承について読んでください。

class Foobar;

class FoobarClient : virtual public Foobar;

class FoobarServer : virtual public Foobar;

class WindowsFoobar : virtual public Foobar;

class UnixFoobar : virtual public Foobar;

ただし、多重継承には多くの問題があります。本当にモデルを提示したい場合は、構築時に参照を作成して参照してから、持ってみFoobarClientませFoobarServerんか?FoobarFoobar& FoobarClient/Server::getFoobar

コンポジションは、多くの場合、多重継承から抜け出す方法です。例を見てみましょう:

class WindowsFoobarClient : public WindowsFoobar 
{
    FoobarClient client;
public:
    WindowsFoobarClient() : client( this ) {}
    FoobarClient& getClient() { return client }
}

ただし、コンストラクタでこれを使用する場合は注意が必要です。

于 2010-01-01T23:41:59.073 に答える
7

あなたがここにいるのは、C++ の仮想継承機能です。あなたがここにいるのは、メンテナンスの悪夢です。H. Sutter のような著名な著者は、このような継承の使用に対してすでにしばらくの間反対しているので、これは大きな驚きではないかもしれません。しかし、これはこのようなコードの直接の経験から来ています。深い継承チェーンを回避します。protectedキーワードを非常に恐れてください- その使用は非常に限られています. この種の設計はすぐに手に負えなくなります - 下位レベルのクラスから継承チェーンのどこか上位にある保護された変数へのアクセスのパターンを追跡するのが難しくなり、コード部分の責任が曖昧になるなど、そしてあなたのコードを毎年見る人々今からあなたを憎むでしょう:)

于 2010-01-02T00:02:04.740 に答える
5

あなたはC++を使用しているので、テンプレートに親しむ必要があります。template-argument-is-a-base-classパターンを使用すると、多重継承や冗長な実装は必要ありません。次のようになります。

class Foobar {};

template <typename Base> class UnixFoobarAspect : public Base {};
template <typename Base> class WindowsFoobarAspect : public Base {};
template <typename Base> class FoobarClientAspect : public Base {};
template <typename Base> class FoobarServerAspect : public Base {};

typedef UnixFoobarAspect<FoobarClientAspect<Foobar>/*this whitespace not needed in C++0x*/> UnixFoobarClient;
typedef WindowsFoobarAspect<FoobarClientAspect<Foobar> > WindowsFoobarClient;
typedef UnixFoobarAspect<FoobarServerAspect<Foobar> > UnixFoobarServer;
typedef WindowsFoobarAspect<FoobarServerAspect<Foobar> > WindowsFoobarServer;

基本クラスが特殊なバリアントの1つに実装された関数を呼び出す必要がある場合は、抽象関数を宣言する代わりに、不思議なことに繰り返されるテンプレートパターンを使用して仮想関数呼び出しを回避することも検討してください。

于 2010-01-02T01:46:16.063 に答える
3

FoobarClientFoobarServerWindowsFoobarおよびの宣言で仮想継承を使用し、基本クラス名の前にUnixFoobar単語を置きます。virtualFoobar

Foobarこれにより、基本クラス階層に何回出現しても、常に単一のインスタンスが存在することが保証されます。

于 2010-01-01T23:38:46.333 に答える
2

この構成と継承の例を試してください。

class Client_Base;
class Server_Base;

class Foobar
{
  Client_Base * p_client;
  Server_Base * p_server;
};

class Windows_Client : public Client_Base;
class Windows_Server : public Server_Base;

class Win32 : Foobar
{
  Win32()
  {
    p_client = new Windows_Client;
    p_server = new Windows_Server;
  }
};

class Unix_Client : public Client_Base;
class Unix_Server : public Server_Base;

class Unix : Foobar
{
  Unix()
  {
    p_client = new Unix_Client;
    p_server = new Unix_Server;
  }
};

多くの専門家は、問題は別のレベルの間接化で解決できると述べています。

于 2010-01-02T00:53:28.307 に答える
2

この検索を見てください。ダイヤモンドの継承はやや議論の余地のある問題であり、適切な解決策は個々の状況によって異なります.

Unix/Windows 側についてコメントしたいと思います。一般#ifndefに、特定のプラットフォームに適していないものを除外します。そのため、 andFoobarではなく、プリプロセッサ ディレクティブを使用して、Windows または Unix 用にコンパイルしただけになります。仮想継承を検討する前に、そのパラダイムを使用してどこまで到達できるかを確認してください。UnixFoobarWindowsFoobar

于 2010-01-01T23:51:05.017 に答える
1

同じ基底クラスを 2 回持つことについて「違法」なことは何もありません。最後の子クラスには、(文字通り) 基本クラスの複数のコピーがその一部として含まれます (基本クラスの各変数などを含む)。ただし、その基本クラスの関数に対するあいまいな呼び出しが発生する可能性があり、手動で解決する必要がある場合があります。これはあなたが望むもののようには聞こえません。

継承ではなく合成を検討してください。

また、仮想継承は、2 回出現する同じ基本クラスをまとめる方法です。ただし、それが本当にデータ共有だけである場合は、構成の方が理にかなっている可能性があります。

于 2010-01-01T23:37:54.803 に答える
0

修飾されたクラス名で変数にアクセスできますが、正確な構文を忘れています。

ただし、これは多重継承を使用する場合の悪いケースの1つであり、多くの問題を引き起こす可能性があります。このように物事を持ちたくない可能性があります。

foob​​arをプライベートに継承したり、各サブクラスにfoobarを所有させたり、foobarを純粋な仮想クラスにしたり、派生クラスに現在定義されているものを所有させたり、さらにはfoobarを独自に定義したりする可能性がはるかに高くなります。

于 2010-01-02T04:33:23.713 に答える