2

次の正しいコピーを作成するために、コピー コンストラクターを呼び出す必要があるクラスがあるとします。

struct CWeird
{
    CWeird() { number = 47; target = &number; }

    CWeird(const CWeird &other) : number(other.number), target(&number) { }

    const CWeird& operator=(const CWeird &w) { number = w.number; return *this; }

    void output()
    {
        printf("%d %d\n", *target, number);
    }

    int *target, number;
};

ここでの問題は、メモリを再割り当てするときに CArray がその要素に対してコピー コンストラクタを呼び出さないことです (古いメモリから新しいメモリへの memcpy のみ)。たとえば、次のコードです。

CArray<CWeird> a;
a.SetSize(1);
a[0].output();

a.SetSize(2);
a[0].output();

結果は

47 47
-572662307 47

わかりません。std::vector は同じオブジェクトを適切にコピーでき、CArray はできないのはなぜですか? ここでの教訓は何ですか?明示的なコピー コンストラクターを必要としないクラスのみを使用する必要がありますか? それとも、重大なことに CArray を使用するのは悪い考えですか?

4

2 に答える 2

3

コピーされたポインターは、サイズ変更により配列が再割り当てされたため、存在しなくなった元の数値を指し続けます。

CArray はコピー構築ではなく代入を使用していると思います。代入演算子を定義して、これで問題が解決するかどうかを確認します。

CWeird& operator=(CWeird w) { swap(w); return *this; }
void swap(CWeird& w) { int t = number; number = w.number; w.number = t; }

コピー構築と割り当ての間で一貫性のない動作を避けるために、とにかくこれを行うことは一般的には良い考えです。

参考までに、上記のコードは、強力な例外安全性を保証する代入セマンティクスを実装するための慣用的なアプローチを使用しています。

  1. const 以外の参照を返すことはoperator=、プリミティブ型のセマンティクスと一致するため、 の標準です。
  2. パラメーターを値で渡すのは、元のコピーを作成する最も簡単な方法であり、コピー コンストラクターが失敗した場合にこのオブジェクトが影響を受けないことを保証します。
  3. swap の呼び出しは、渡されたコピーをこのオブジェクトに切り替えて、決して例外をスローしないようにするため、完全に例外セーフな方法で代入が行われます。

この場合、番号を割り当てるだけの方が簡単ですが、将来のメンテナーがコピーで例外をスローできるようにした場合にズボンを下ろすことを避けるために、すべての割り当てをこの方法で習慣的に実装しています。

于 2010-05-28T11:06:29.753 に答える