18

operator= の継承に問題があります。このコードが機能しない理由と、それを修正する最善の方法は何ですか?

#include <iostream>

class A
{
public:
    A & operator=(const A & a)
    {
        x = a.x;
        return *this;
    }

    bool operator==(const A & a)
    {
        return x == a.x;
    }

    virtual int get() = 0; // Abstract

protected:
    int x;
};

class B : public A
{
public:
    B(int x)
    {
        this->x = x;
    }

    int get()
    {
        return x;
    }
};

class C : public A
{
public:
    C(int x)
    {
        this->x = x;
    }

    int get()
    {
        return x;
    }
};

int main()
{
    B b(3);
    C c(7);
    printf("B: %d C: %d B==C: %d\n", b.get(), c.get(), b==c);

    b = c; // compile error
    // error: no match for 'operator= in 'b = c'
    // note: candidates are B& B::operator=(const B&)

    printf("B: %d C: %d B==C: %d\n", b.get(), c.get(), b==c);
    return 0;
}
4

5 に答える 5

35

クラスでコピー代入演算子を宣言しない場合、コンパイラは暗黙的に宣言します。暗黙的に宣言されたコピー代入演算子は、継承された代入演算子を非表示にします (C++ での「名前の非表示」について読んでください)。つまり、継承された代入演算子は、修飾されていない名前検索プロセスからは「見えなくなり」ます (これは、実行したときに起こることですb = c) 。 、それらを「再表示」するための特定の手順を実行しない限り。

あなたの場合、クラスBには明示的に宣言されたコピー代入演算子がありません。これは、コンパイラが宣言することを意味します

B& B::operator =(const B&)

暗黙的に。から継承された演算子を非表示にしAます。この線

b = c;

ここでの唯一の候補は、上記の暗黙的に宣言されたものであるため、コンパイルされませんB::operator =(コンパイラーは既にそれについて通知しています)。他のすべての候補は非表示になります。はcに変換できB&ないため、上記の代入はコンパイルされません。

コードをコンパイルしたい場合は、using-declaration を使用して、継承A::operator =されたものを追加して非表示にできます。

using A::operator =;

class の定義にB。コードはコンパイルされますが、スタイルは良くありません。この場合、b = c割り当てによって が呼び出され、関連するオブジェクトの部分A::operator =のみが割り当てられることに注意してください。A(しかし、明らかにそれはあなたの意図です。)

または、このような場合は、名前の修飾バージョンを使用して、名前の隠蔽をいつでも回避できます。

b.A::operator =(c);
于 2010-10-07T14:08:41.887 に答える
3

何が起こっているかというoperator =と、コンパイラがクラスを持たないクラスに対して生成するデフォルトは、基本クラスを隠しているということoperator =です。この特定のケースでは、コンパイラがconst B &B::operator =(const B &)舞台裏で生成しています。あなたの割り当てはこの演算子と一致し、で宣言したものを完全に無視しますclass A。aC&は a に変換できないためB&、コンパイラは表示されるエラーを生成します。

今は面倒に思えますが、あなたはこれが起こることを望んでいます。あなたが書いたようなコードが機能しなくなります。関係のない型 (B と C には共通の祖先がありますが、継承で重要な関係は兄弟関係ではなく、親子関係のみです) を 1 つに割り当てることができるため、そのようなコードは機能させたくありません。別。

ISA の観点から考えてみましょう。両方であるという理由だけでCar、 a を a に割り当てることを許可する必要がありますか?BoatVehicles

このようなものを作成するには、封筒/レターパターンを使用する必要があります。エンベロープ (別名ハンドル) は、特定の基本クラス (レター) から派生したクラスのインスタンスを保持することだけを行う特殊なクラスです。ハンドルは、含まれているオブジェクトへの代入以外のすべての操作を転送します。割り当ての場合、内部オブジェクトのインスタンスを、割り当てられた from オブジェクトのコピー構築された (「クローン」メソッド (別名仮想コンストラクター) を使用) コピーに置き換えるだけです。

于 2010-10-07T13:41:31.090 に答える
1

通常、operator= は B で次のように定義されます。

B& operator=(B const &);

B は明確でアクセス可能な 'C' のベースではないため、C から B への変換はコンパイラによって許可されません。

本当に「C」を「B」に代入したい場合、「B」は適切な代入演算子を次のようにサポートする必要があります。

B& operator=(C const &);
于 2010-10-07T13:55:48.850 に答える
1

(おそらく修正ではなく、おそらくすべきことではありません)しかし...本当に必要な場合は、問題を強制的に実行できる方法があります。

 (A&)(*(&b)) = (A&)(*(&c))
于 2010-10-07T14:37:33.960 に答える
1

このように階層全体で割り当てることはできません。B と C は A の異なるサブクラスです。B を B に、または C を C に割り当てることはできますが、C を B に割り当てることはできません。

ただし、これを試す前operator=に、割り当ての A 部分を委任して、B と Cに実装することをお勧めします。A::operator=そうしないと、これらのクラスの B および C 固有の部分が代入で失われます。

于 2010-10-07T13:42:16.160 に答える