12

なんらかの理由で、クラスにコピー コンストラクターと operator= の両方を提供する必要があります。operator=コピーctorを定義すればいらないと思っていたのですが、欲しくQListなりました。それはさておき、私はコードの重複が嫌いなので、このようにして何か問題はありますか?

Fixture::Fixture(const Fixture& f) {
    *this = f;
}

Fixture& Fixture::operator=(const Fixture& f) {
    m_shape         = f.m_shape;
    m_friction      = f.m_friction;
    m_restitution   = f.m_restitution;
    m_density       = f.m_density;
    m_isSensor      = f.m_isSensor;
    return *this;
}

そして、好奇心から、コードの大部分がコピー ctor にあり、operator=何らかの形でそれを利用するように切り替える方法はありませんか? 私は試しreturn Fixture(f);ましたが、それは好きではありませんでした。


コピー コンストラクターと代入演算子が、継承元のクラスによって暗黙的に無効にされていることをより明確にする必要があるようです。なんで?これは、単独でインスタンス化するべきではない抽象基本クラスであるためです。ただし、このクラススタンドアロンを想定しています。

4

6 に答える 6

26

operator=セットアップオブジェクトに依存できなくなったため、これは悪いことです。逆の方法で行う必要があり、コピースワップイディオムを使用できます。

すべての要素をコピーする必要がある場合は、暗黙的に生成された代入演算子を使用できます。

その他の場合は、さらに何かを行う必要があります。ほとんどの場合、メモリを解放してコピーします。これは、コピースワップイディオムが適しているところです。エレガントであるだけでなく、プリミティブを交換するだけの場合に割り当てが例外をスローしないように提供します。コピーする必要のあるバッファを指すクラスを見てみましょう。

Fixture::Fixture():m_data(), m_size() { }

Fixture::Fixture(const Fixture& f) {
    m_data = new item[f.size()];
    m_size = f.size();
    std::copy(f.data(), f.data() + f.size(), m_data);
}

Fixture::~Fixture() { delete[] m_data; }

// note: the parameter is already the copy we would
// need to create anyway. 
Fixture& Fixture::operator=(Fixture f) {
    this->swap(f);
    return *this;
}

// efficient swap - exchanging pointers. 
void Fixture::swap(Fixture &f) {
    using std::swap;
    swap(m_data, f.m_data);
    swap(m_size, f.m_size);
}

// keep this in Fixture's namespace. Code doing swap(a, b)
// on two Fixtures will end up calling it. 
void swap(Fixture &a, Fixture &b) {
  a.swap(b);
}

それが私が通常代入演算子を書く方法です。スピードが欲しいですか?異常な代入演算子の署名に関する値の受け渡し(値の受け渡し)

于 2009-09-22T02:33:51.687 に答える
8

Copy ctorと割り当ては完全に異なります。割り当ては通常、置き換えるオブジェクトのリソースを解放する必要があります。copyctorは、まだ初期化されていないオブジェクトで動作しています。ここでは明らかに特別な要件はないので(割り当てに「リリース」は必要ありません)、アプローチは問題ありません。より一般的には、「オブジェクトが保持しているすべてのリソースを解放する」補助メソッド(dtorで、割り当ての開始時に呼び出される)と、「これらの他のものをオブジェクトにコピーする」部分が適度に近い場合があります。典型的なコピーコンストラクタの仕事に(またはほとんどの場合、とにかく;-)。

于 2009-09-22T02:29:04.647 に答える
6

例では、メンバーごとのコピーと割り当てを行っているだけです。これは、自分で書く必要があるものではありません。コンパイラは、まさにそれを行う暗黙のコピー操作と代入操作を生成できます。独自のコピー コンストラクタ、代入、および/またはデストラクタを作成する必要があるのは、コンパイラによって生成されたものが適切でない場合 (つまり、ポインタなどを介してリソースを管理する場合) だけです。

于 2009-09-22T07:14:17.990 に答える
3

あなたのoperator=が仮想になると、問題が発生すると思います。

コピーを実行し、代わりにcopy-constructorとoperator =がその関数を呼び出す関数(おそらく静的)を作成することをお勧めします。

于 2009-09-22T02:30:37.140 に答える
2

はい、これは良い習慣であり、(ほぼ)常に実行する必要があります。さらに、デストラクタとデフォルト コンストラクタをトスします (非公開にしても)。

James Coplien の 1991 年の書籍Advanced C++ では、これは「正統な標準形式」の一部として説明されています。その中で、彼はデフォルト コンストラクター、コピー コンストラクター、代入演算子、およびデストラクターを提唱しています。

一般に、次の場合はオーソドックスな正規形を使用する必要があります。

  • クラスのオブジェクトの割り当てをサポートするか、これらのオブジェクトを値渡しパラメーターとして関数に渡したい場合、および
  • オブジェクトに、参照カウントされるオブジェクトへのポインターが含まれているか、クラス デストラクターがdeleteオブジェクトのデータ メンバーに対して a を実行します。

プログラム内の重要なクラスには正統な標準形式を使用する必要があります。これは、クラス間の均一性を確保し、プログラムの進化の過程で各クラスの複雑さが増していくのを管理するためです。

Coplien は、このパターンの理由のページを提供していますが、ここでは説明しきれませんでした。ただし、既に触れた重要な項目は、上書きされるオブジェクトをクリーンアップする機能です。

于 2009-09-22T02:51:04.300 に答える
1

を使用してオブジェクトメンバー変数を初期化する必要があると思いますinitializer list。変数が の場合primitive-types、それは問題ではありません。それ以外の場合、割り当ては初期化とは異なります。


copy constructorto内のポインターを初期化することでちょっとしたトリックでそれを行うことができます0。次に、内で delete を安全に呼び出すことができますassignment operator

Fixture::Fixture(const Fixture& f) : myptr(0) {
    *this = f;
}
Fixture& Fixture::operator=(const Fixture& f) {
    // if you have a dynamic array for example, delete other wise.
    delete[] myptr;
    myptr = new int[10];
    // initialize your array from the other object here.
    ......
    return *this;
}
于 2009-09-22T02:28:48.890 に答える