2

私は典型的なダイヤモンドの問題のように仮想継承を使用しています:仮想基本クラスの代入演算子が1回だけ呼び出されるようにするにはどうすればよいですか?

  A 
(virtual)/ \ (virtual) 
     B  C 
      \ /
      D 

私はすべてのクラスで「deep_copy_from」という名前のメソッドを実装しています(しかし、それは同様=()代入演算子することができます)。メソッドは、クラス自身の属性をコピーし、そのコピーを上記のクラスに伝播する必要があります。

問題は、Dインスタンスを深くコピーしているときにA :: deep_copy_fromメソッドが2回呼び出されてしまうことです(Aの「バージョン」が1つしかないため、一度呼び出す必要があります)。一度しか呼び出されないようにするにはどうすればよいでしょうか?

(B :: deep_copy_fromとC :: deep_copy_fromは同じように動作する必要があります)。ここ

は、サンプルコードである:ここ

class A 
{ 
public: 
    A(string const& p_a_name) : a_name(p_a_name) { 
     cout << "A(a_name=\"" << p_a_name << "\")" << endl; 
    } 

    virtual void deep_copy_from(A const& a) 
    { 
     cout << "A::deep_copy_from(A(a_name=\"" << a.a_name << "\"))" << endl; 
     this->a_name = a.a_name; 
    } 

protected: 
    string a_name; 
}; 

class B : public virtual A 
{ 
public: 
    B(string const &p_a_name, string const& p_b_name) : A(p_a_name), b_name(p_b_name) { 
     cout << "B(a_name=\"" << p_a_name << "\", b_name=\"" << p_b_name << "\")" << endl; 
    } 

    virtual void deep_copy_from(B const& b) 
    { 
     cout << "B::deep_copy_from(B(a_name=\"" << b.a_name << "\", b_name=\"" << b.b_name << "\"))" << endl; 
     this->A::deep_copy_from(static_cast<A const&>(b)); 
     this->b_name = b.b_name; 
    } 

protected: 
    string b_name; 
}; 

class C : public virtual A 
{ 
public: 
    C(string const &p_a_name, string const& p_c_name) : A(p_a_name), c_name(p_c_name) { 
     cout << "C(a_name=\"" << p_a_name << "\", c_name=\"" << p_c_name << "\")" << endl; 
    } 

    virtual void deep_copy_from(C const& c) 
    { 
     cout << "C::deep_copy_from(C(a_name=\"" << c.a_name << "\", c_name=\"" << c.c_name << "\"))" << endl; 
     this->A::deep_copy_from(static_cast<A const&>(c)); 
     this->c_name = c.c_name; 
    } 

protected: 
    string c_name; 
}; 

class D : public B, public C 
{ 
public: 
    D(string const &p_a_name, string const& p_b_name, string const& p_c_name, string const& p_d_name) 
     : A(p_a_name), B(p_a_name, p_b_name), C(p_a_name, p_c_name), d_name(p_d_name) 
    { 
     cout << "D(a_name=\"" << p_a_name << "\", b_name=\"" << p_b_name 
      << "\", c_name=\"" << p_c_name << "\", d_name=\"" << p_d_name << "\")" << endl; 
    } 

    virtual void deep_copy_from(D const& d) 
    { 
     cout << "D::deep_copy_from(D(a_name=\"" << d.a_name << "\", b_name=\"" << d.b_name 
      << "\", c_name=\"" << d.c_name << "\", d_name=\"" << d.d_name << "\"))" << endl; 
     this->B::deep_copy_from(static_cast<B const&>(d)); 
     this->C::deep_copy_from(static_cast<C const&>(d)); 
     this->d_name = d.d_name; 
    } 

protected: 
    string d_name; 
}; 

電流出力である:

A(a_name="A") 
B(a_name="A", b_name="B") 
C(a_name="A", c_name="C") 
D(a_name="A", b_name="B", c_name="C", d_name="D") 
D::deep_copy_from(D(a_name="A", b_name="B", c_name="C", d_name="D")) 
B::deep_copy_from(B(a_name="A", b_name="B")) 
A::deep_copy_from(A(a_name="A")) 
C::deep_copy_from(C(a_name="A", c_name="C")) 
A::deep_copy_from(A(a_name="A")) 

更新:

現在のバージョンは、次のようになる:

class A 
{ 
public: 
    A(string const& p_a_name) : a_name(p_a_name) { 
     cout << "A(a_name=\"" << p_a_name << "\")" << endl; 
    } 

    virtual void deep_copy_from(A const& a) 
    { 
     cout << "A::deep_copy_from(A(a_name=\"" << a.a_name << "\"))" << endl; 
     this->a_name = a.a_name; 
    } 

protected: 
    string a_name; 
}; 

class B : public virtual A 
{ 
public: 
    B(string const &p_a_name, string const& p_b_name) : A(p_a_name), b_name(p_b_name) { 
     cout << "B(a_name=\"" << p_a_name << "\", b_name=\"" << p_b_name << "\")" << endl; 
    } 

    virtual void deep_copy_from(B const& b) 
    { 
     cout << "B::deep_copy_from(B(a_name=\"" << b.a_name << "\", b_name=\"" << b.b_name << "\"))" << endl; 
     this->A::deep_copy_from(static_cast<A const&>(b)); 
     this->deep_copy_my_bits_from(b); 
    } 

protected: 
    void deep_copy_my_bits_from(B const& b) { 
     cout << "B::deep_copy_my_bits_from(B(a_name=\"" << b.a_name << "\", b_name=\"" << b.b_name << "\"))" << endl; 
     this->b_name = b.b_name; 
    } 

protected: 
    string b_name; 
}; 

class C : public virtual A 
{ 
public: 
    C(string const &p_a_name, string const& p_c_name) : A(p_a_name), c_name(p_c_name) { 
     cout << "C(a_name=\"" << p_a_name << "\", c_name=\"" << p_c_name << "\")" << endl; 
    } 

    virtual void deep_copy_from(C const& c) 
    { 
     cout << "C::deep_copy_from(C(a_name=\"" << c.a_name << "\", c_name=\"" << c.c_name << "\"))" << endl; 
     this->A::deep_copy_from(static_cast<A const&>(c)); 
     this->deep_copy_my_bits_from(c); 
    } 

protected: 
    void deep_copy_my_bits_from(C const& c) { 
     cout << "C::deep_copy_my_bits_from(C(a_name=\"" << c.a_name << "\", c_name=\"" << c.c_name << "\"))" << endl; 
     this->c_name = c.c_name; 
    } 

protected: 
    string c_name; 
}; 

class D : public B, public C 
{ 
public: 
    D(string const &p_a_name, string const& p_b_name, string const& p_c_name, string const& p_d_name) 
     : A(p_a_name), B(p_a_name, p_b_name), C(p_a_name, p_c_name), d_name(p_d_name) 
    { 
     cout << "D(a_name=\"" << p_a_name << "\", b_name=\"" << p_b_name 
      << "\", c_name=\"" << p_c_name << "\", d_name=\"" << p_d_name << "\")" << endl; 
    } 

    virtual void deep_copy_from(D const& d) 
    { 
     cout << "D::deep_copy_from(D(a_name=\"" << d.a_name << "\", b_name=\"" << d.b_name 
      << "\", c_name=\"" << d.c_name << "\", d_name=\"" << d.d_name << "\"))" << endl; 
     this->A::deep_copy_from(static_cast<A const&>(d)); 
     this->B::deep_copy_my_bits_from(static_cast<B const&>(d)); 
     this->C::deep_copy_my_bits_from(static_cast<C const&>(d)); 
     this->d_name = d.d_name; 
    } 

protected: 
    string d_name; 
}; 

そして、出力は次のようになります。

A(a_name="A") 
B(a_name="A", b_name="B") 
C(a_name="A", c_name="C") 
D(a_name="A", b_name="B", c_name="C", d_name="D") 
D::deep_copy_from(D(a_name="A", b_name="B", c_name="C", d_name="D")) 
A::deep_copy_from(A(a_name="A")) 
B::deep_copy_my_bits_from(B(a_name="A", b_name="B")) 
C::deep_copy_my_bits_from(C(a_name="A", c_name="C")) 

私はそれよりももっと良いものを得ることができますか? (より自動)

+1

これまではこれまでやったことはありませんが、BとCで 'deep_copy_from'を呼び出すのではなく、A、B、Cで' deep_copy_just_my_bit'を呼び出すことができました。言語は、例えば、Aのコンストラクタ/デストラクタはDにアクセス可能である必要がありますが、非仮想ベースのコン/デストラクタは直接の派生クラスからアクセス可能である必要があります。工事はあなたがここで望むように、同じクラスのプロパティごとに同じことをします。言語が特別なルールなしに問題を解決できない場合、私はどちらかといえます。 –

+1

このように継承を使用する必要がありますか? 「is a」から「a have」までの関係の少なくとも一部を改造することは貴重な時間かもしれません。 – Patrick

+0

@スティーブ、コメントではなく答えとして答えたら、あなたは受け入れられるでしょう;) –

答えて

2

@Alfは割り当てに関する権利である:割り当てがねじ込まれています。なぜなら、それはバイナリ演算であり、共分散問題のためにOOフレームワークでバイナリ演算をディスパッチすることは不可能であるからである。

あなたの質問には一般的な回答がありますが、最初に知る必要があることが2つあります。第一に、スタンダードは何を宣言しても、スタンダードが間違っていても、仮想ベースは常に公開されているということです。 [証明:別のクラスを派生させて、再び仮想基盤の公開バーチャルを宣言し、アクセス権を持つ]

第2の事実は、仮想基底が間接的な基底のすべてのクラスの直接基底であるということです。それが間違っているので、標準を無視してください。上記を参照。これら二つの事実を考えると

は、それは重複を避けるために適切なパターンを簡単に確認することができ:

ここにあなたのダイヤモンドです:

struct A { cp(){ "A" } virtual CP(){ cp(); } }; 
struct B : virtual A { cp(){ "B" } CP() { cp(); A::CP(); } }; 
struct C : ... ibid ... 
struct D : B, C, virtual A { 
    cp() { "D"; B::cp(); C::cp(); } 
    CP() { cp(); A::cp(); } 
}; 

私は簡潔にするために戻り値の型や他のものをオフに左。 cp()関数 は、最初にメンバーを処理してから、それぞれの非仮想の ベースを呼び出してメンバーを再帰的に処理します。実際には、公開されていないので、 を保護する必要があります。間接的な非仮想ベースに直接アクセスすることはできず、直接的なものだけをアクセスすることができないため、ドリルダウンは必須です。

CP()関数は仮想です。ダイヤルにアクセスしているポインタ(A、B、C、またはD)に関係なく、すべての呼び出しは固有のオブジェクト CPに移動します。

それは(CPを呼び出すことにより、すべてのメンバー及び非仮想ベースサブオブジェクトのメンバーを処理する)独自のクラスの が、それは仮想塩基を処理し、この場合にのみ ものがある、すなわちA.

あなたの動的な型はDと静的な型がBでされている場合、あなたは以下となります。あなたが任意のポインタオフ完全なオブジェクトのクローンを作成し、同じ動的および静的型を取り戻すことができるかどうかX :: CPは()、その後

X *X::clone() const; 

になった場合あなたが始めたときとまったく同じように、B *をDオブジェクトに取得します。

このように割り当てを行うことはできません。割り当て作業をまったく行うことはできません。その理由は、代入が2つの引数に対して共変(covariant)であるためです。ソースとターゲットのダイナミックタイプが同じであることを保証する方法はなく、割り当てが機能するためには必要です。ソースが大きすぎると、その一部がスライスされます。ターゲットが大きすぎると、そのうちのいくつかは割り当てられません。したがって、どのオブジェクト(ターゲットまたはソース)にディスパッチしても違いはありません。それは機能しません。働くことができる唯一の種類の割り当ては、静的型に基づく非仮想の割り当てです。それは、スライスを超えても下回ってもかまいませんが、少なくとも問題は静的に明らかです。

クローニングは、1つの引数(つまり、自己オブジェクト)を持つ関数であるために機能します。一般的に、 "値のもの"とは対照的に "オブジェクトのもの"を使用している場合は、実際に値を操作できるだけなので、ポインタを使用する必要があります。その場合、クローン()と友人はあなたが望むものだけです:ポインタを割当てることができます!

0

deep_copy_fromは共変型ではありません。戻り型でのみ共分散を使用できます。

作成したコードで「仮想関数のオーバーロードを隠す」警告が表示されることがあります。

Aバージョンを呼び出さずにBまたはCバージョンを呼び出す方法はありません.BまたはCを変更しない限り、BバージョンとCバージョンの両方を呼び出す必要がある場合は、Aバージョンが2回呼び出されることは避けられません。 。

BとCの両方がAから仮想を継承しているとすれば、最終的なクラスがAの部分を担当するため、おそらくAバージョンを呼び出さないといけません。

1

2つの問題があります.1つの部分への二重コピーに関するものと、仮想割り当てopに関するものです。

バーチャル割り当て:コンパイル時から実行時にエラー検出を転送するため、仮想的な割り当てはお勧めしません。単にしないでください。仮想割り当て(またはあなたのような割り当てのようなop)が必要と思われる一般的な解決策は、代わりに複製を実装することです。動的に割り当てられたコピーを生成する仮想関数cloneメンバ関数です。

ダブルコピー:簡単な答えは、建設の観点からの割り当てを表すことです。これを行うための慣用的な方法は、「スワップイディオム」として知られています。単に構造体をコピーし、次にスワップの内容を現在のインスタンスで作成し、作成したインスタンスのデストラクタをクリーンアップします。

乾杯& HTH、

+0

あらかじめ割り当てられたスペースが多いので、スワップを使いたくないので、deep_copy_fromはこれを(可能な限り)再利用し、内容を上書きします。 –

関連する問題