3

典型的なダイヤモンドの問題のように、仮想継承を使用しています。

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

すべてのクラスに「deep_copy_from」という名前のメソッドを実装しています (ただし、代入 operator=() の場合もあります)。このメソッドは、クラス独自の属性をコピーし、そのコピーを上のクラスに伝達する必要があります。

問題は、D インスタンスをディープ コピーしているときに A::deep_copy_from メソッドが 2 回呼び出されることです (A の「バージョン」は 1 つしかないため、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"))

それよりも良いものを手に入れることはできますか?(つまり、より自動化された)

4

3 に答える 3

2

@Alfは割り当てについて正しいです:割り当てはねじ込まれています。その理由は、これが二項演算であり、共分散の問題により、オブジェクト指向フレームワークで二項演算をディスパッチすることが不可能だからです。

これで、質問に対する一般的な回答が得られましたが、まず知っておくべきことが 2 つあります。1 つ目は、何を宣言しても、標準が何を言おうとも、仮想ベースは常にパブリックであるということです。標準は間違っています。[証明: 別のクラスを派生させ、任意の仮想ベース public virtual を再度宣言するだけで、アクセスできます]

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 という 1 つのみがあります。

X::CP() が

X *X::clone() const;

次に、任意のポインターから完全なオブジェクトを複製し、同じ動的型と静的型を取得できる場合: 動的型が D で静的型が B の場合、最初とまったく同じように B* を D オブジェクトに取得します。

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

クローンは引数が 1 つしかない関数 (つまり、自己オブジェクト) であるため、機能します。一般に、「値のもの」ではなく「オブジェクトのもの」を使用している場合、実際には値しか操作できないため、ポインターを使用する必要があります。その場合、 clone() とその友達はまさにあなたが望むものです:ポインタをうまく割り当てることができます!

于 2010-12-06T18:42:49.913 に答える
1

2 つの問題があります。1 つは A 部分への二重コピーに関するもので、もう 1 つは仮想代入 op に関するものです。

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

二重コピー: 簡単な答えは、構成の観点から代入を表現することです。これを行う慣用的な方法は、「スワップ イディオム」として知られています。簡単に言えば、コピーを構築し、その内容を現在のインスタンスと交換し、構築したインスタンスのデストラクタにクリーンアップを任せます。

乾杯 & hth.,

于 2010-12-06T16:37:15.713 に答える
0

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

作成したコードで「オーバーロードが仮想関数を非表示にします」という警告が表示される場合があります。

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

B と C の両方が A から virtual を継承することを考えると、最終クラスが A 部分を担当するため、おそらく A バージョンを呼び出す必要はありません。

于 2010-12-06T13:45:17.663 に答える