0

私は学び始めたばかりでRule of Three、次のアプローチがコピーコンストラクターに十分かどうか疑問に思っていました:

Array<T, ROW, COL>(const Array<T, ROW, COL> &array) {
    *this = array;
}

十分でない場合、その理由は何ですか?

EDITリクエストごとに代入演算子を追加しました

inline Array<T, ROW, COL>& operator=(const Array<T, ROW, COL> &other) {;
    for (int i = 0; i < ROW; ++i) {
        for (int j = 0; j < COL; ++j) {
            data[j*ROW + i] = other(i, j);
        }
    }

    return *this;
}
4

3 に答える 3

4

一般的な注意事項(質問を編集する前):

コピー コンストラクターでコピー代入演算子を呼び出すだけでは、一般的な解決策にはなりません。それらは異なることを意味します (つまり、同じことを意味しているのに、なぜそこにある必要があるのでしょうか)。

  • 代入演算子は、(既存の!) オブジェクトに新しい値が割り当てられるときに呼び出されます。このオブジェクトが同じタイプの場合、これをコピー代入とも呼びます。これは、典型的な実装ではコンテンツをコピーするだけだからです (ただし、共有ポインターや共有データを持つ PIMPL クラスなど、参照されるものを共有する場合があります)。コピー代入演算子は、指定しない限り、コンパイラによって自動的に実装されます。型の代入演算子を使用して各メンバーをコピーします (プリミティブ メンバーもコピーされます)。

  • コピー コンストラクターは、(まだ存在しない!) オブジェクトに同じ型の初期値が割り当てられたときに呼び出されます。つまり、既存のオブジェクトのコピーで初期化する必要があります。繰り返しになりますが、コピー コンストラクターを指定しない場合、コンパイラはコピー コンストラクターを使用してメンバーをコピーするだけで、コピー コンストラクターを生成します。

コピー コンストラクター内から代入演算子を呼び出す場合、これは、生成されたプログラムが、新しいオブジェクトをコピー初期化するときに次の手順を実行することを意味します。

  • (メンバー初期化リストを使用しない限り:) デフォルトのコンストラクターで非プリミティブ クラス メンバーを初期化します。プリミティブ型は初期化されていません。
  • 次に、代入演算子が呼び出されます。定義しなかった場合は、すべてのメンバーがコピーされます。

したがって、ほとんどの場合は問題ありませが、これが機能しないケースがいくつかあります。特に、デフォルトで構築できない、または割り当てできないメンバーがクラスに含まれている場合です。この場合、(コピー代入ではなく) コピー構築できる場合は、コピー コンストラクターのメンバー初期化リストでメンバーを手動で初期化する必要があります。


編集(質問が編集されたため):あなたの場合、dataプリミティブ型(すべてのポインターはプリミティブと見なされます)であるため、代入演算子を呼び出す前にコピーコンストラクターで適切に初期化する必要があります。そうしないと、割り当てによって初期化されていないポインターが削除されます。したがって、最善を尽くす必要があります(コードの重複を避けるため。そうすればより効率的です):

Array<T, ROW, COL>(const Array<T, ROW, COL> &array) :
    data(0)  // Now it is at least initialized, although inefficient
{
    *this = array;
}

次に、代入演算子は null ポインターを削除しようとしますが、これは問題なく (何もしないだけです)、実際のコピーを実行します。data(0)初期化を「デフォルトで構築された null Array<...>」オブジェクトとして、一時的に考えてください。(おそらく、外部メモリを割り当てないnull オブジェクトを既に提供していますか?)

于 2013-06-06T13:00:37.737 に答える