メインコンテンツ

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

*this はコピー代入演算子に返されません

operator= メソッドが現在のオブジェクトを指すポインターを返さない

説明

この欠陥は、operator=operator+= などの代入演算子が *this への参照を返さない場合に発生します。ここで、this は現在のオブジェクトを指すポインターです。operator= メソッドが *this を返さない場合、これは、a=b または a.operator=(b) が代入後に代入先 a を返していないことを意味します。

次に例を示します。

  • 演算子が、それ自体のパラメーターを現在のオブジェクトへの参照の代わりに返します。

    つまり、演算子が取る形式は、MyClass & operator=(const MyClass & rhs) { ... return *this; } ではなく MyClass & operator=(const MyClass & rhs) { ... return rhs; } です。

  • 演算子を参照ではなく、値で返します。

    つまり、演算子が取る形式は、MyClass & operator=(const MyClass & rhs) { ... return *this; } ではなく MyClass operator=(const MyClass & rhs) { ... return *this; } です。

リスク

通常、オブジェクトの代入は組み込み型間での代入のように動作するものと想定され、また、代入により代入先が返されるものと想定されます。たとえば、右結合の連鎖代入 a=b=c では、代入後に b=c が代入先 b を返すよう求められます。代入演算子が異なった動作をする場合、ユーザー クラスには予期しない結果がもたらされる可能性があります。

予期しない結果は、代入が別のステートメントの一部である場合に発生します。次に例を示します。

  • operator= が現在のオブジェクトへの参照ではなく、それ自体のパラメーターを返す場合、代入 a=ba ではなく b を返します。operator= がデータ メンバーの部分的な代入を実行する場合、代入 a=b の後では ab のデータ メンバーは異なります。ユーザー クラスで戻り値のデータ メンバーを読み取る際に a のデータ メンバーを想定している場合は、予期しない結果になる可能性があります。例は、引数と同じ operator= の戻り値を参照してください。

  • operator= メソッドが *this を参照ではなく値で返す場合は、*this のコピーが返されます。代入の結果を (a=b).modifyValue() のようなステートメントを使用して変更する場合、変更するのは a のコピーであり、a そのものではありません。

修正方法

*this を代入演算子から返すようにします。

すべて展開する

class MyClass {
    public:
        MyClass(bool b, int i): m_b(b), m_i(i) {}
        const MyClass& operator=(const MyClass& obj) {
            if (&obj!=this) {
                /* Note: Only m_i is copied. m_b retains its original value. */
                m_i = obj.m_i;
            }
            return obj;
        }
        bool isOk() const { return m_b;}
        int getI() const { return m_i;}
    private:
        bool m_b;
        int m_i;
};

void main() {
        MyClass r0(true, 0), r1(false, 1);
        /* Object calling isOk is r0 and the if block executes. */
        if ( (r1 = r0).isOk()) {
            /* Do something */
        }
}

この例において、演算子 operator=*this への参照ではなく、それ自体の現在の引数を返します。

したがって、main で代入 r1 = r0 が返すのは r0 であり、r1 ではありません。operator= ではデータ メンバー m_b をコピーしないため、r0.m_br1.m_b の値は異なります。次のような予期しない動作が発生します。

予期されている動作実際に発生する動作
  • ステートメント (r1 = r0).isOk() が、値 false をもつ r1.m_b を返す

  • if ブロックは実行されない。

  • ステートメント (r1 = r0).isOk() が、値 true をもつ r0.m_b を返す

  • if ブロックが実行される。

修正 — *this を返す

1 つの修正方法として、*thisoperator= から返します。

class MyClass {
    public:
        MyClass(bool b, int i): m_b(b), m_i(i) {}
        const MyClass& operator=(const MyClass& obj) {
            if (&obj!=this) {
                /* Note: Only m_i is copied. m_b retains its original value. */
                m_i = obj.m_i;
            }
            return *this;
        }
        bool isOk() const { return m_b;}
        int getI() const { return m_i;}
    private:
        bool m_b;
        int m_i;
};

void main() {
        MyClass r0(true, 0), r1(false, 1);
        /* Object calling isOk is r0 and the if block executes. */
        if ( (r1 = r0).isOk()) {
            /* Do something */
        }
}

結果情報

グループ: オブジェクト指向
言語: C++
既定値: オフ
コマンド ライン構文: RETURN_NOT_REF_TO_THIS
影響度: Low

バージョン履歴

R2015b で導入