4

これは、operator =の観点からコピーコンストラクターを実装することの複製ではありませんが、より具体的な質問です。(またはそう思うのが好きです。)

イントロ

このような(架空の)クラスが与えられた場合:

struct FooBar {
  long id;
  double valX;
  double valZ;
  long   valN;
  bool   flag; 
  NonCopyable implementation_detail; // cannot and must not be copied

  // ...
};

構成をコピーしたり、NonCopyableオブジェクトをコピーしたりすることはできないため、デフォルトで生成された関数でこれをコピーすることはできません。ただし、オブジェクトのこの部分は、実際にはコピーする必要のない実装の詳細です。

また、このためのスワップ関数を作成しても意味がありません。これは、スワップ関数がstd :: swapの機能を複製できるためです(NonCopyableを除く)。

したがって、これらのオブジェクトをコピーする場合は、copy-ctorとcopy-operatorを自分で実装する必要があります。これは、他のメンバーを割り当てるだけで簡単に実行できます。

質問

copy ctorとoperatorを実装する必要がある場合、copy演算子の観点からcopy ctorを実装する必要がありますか、それとも初期化リストを使用してコードを「複製」する必要がありますか?

つまり、与えられた:

FooBar& operator=(FooBar const& rhs) {
  // no self assignment check necessary
  id = rhs.id;
  valX = rhs.valX;
  valZ = rhs.valZ;
  valN = rhs.valN;
  flag = rhs.flag;
  // don't copy implementation_detail
  return *this;
}

私たちはa)を書くべきですか

FooBar(FooBar const& rhs) {
  *this = rhs;
}

またはb)

FooBar(FooBar const& rhs)
: id(rhs.id)
, valX(rhs.valX)
, valZ(rhs.valZ)
, valN(rhs.valN)
, flag(rhs.flag)
// don't copy implementation_detail
{ }

答えの考えられる側面は、パフォーマンス対保守性対可読性です。

4

5 に答える 5

5

通常、コピーコンストラクター(@Roger Pateのバージョン)の観点から代入演算子を実装します。

FooBar& operator=(FooBar copy) { swap(*this, copy); return *this; }
friend void swap(FooBar &a, FooBar &b) {/*...*/}

これには、swap関連するメンバーを交換する機能を提供する必要があります(あなたの場合を除くすべてimplementation_detail)。

がスローされない場合swap、このアプローチは、オブジェクトが一貫性のない状態のままにならないことを保証します(一部のメンバーのみが割り当てられます)。

ただし、あなたの場合、コピーコンストラクターも代入演算子も代入演算子の観点からコピーコンストラクターの実装をスローできないため(a)も問題なく、両方の場所にほぼ同じコードを配置するよりも保守しやすくなります(b)。

于 2010-11-08T07:40:52.343 に答える
1

一般に、メンバーのデフォルトの構成を明示的に回避するため、a)よりもb)の方が好きです。int、doubleなどの場合は考慮されませんが、高額な操作や副作用のあるメンバーの場合は考慮されます。メンバーを追加および削除するときに、この潜在的なコスト/問題を考慮する必要がない場合は、より保守しやすくなります。イニシャライザーリストは、参照とデフォルトで構成できない要素もサポートします。

または、「実装の詳細」以外のメンバーのサブ構造を作成し、コンパイラーに次の行に沿ってコピーコードを生成させることもできます。

struct X 
{
    struct I
    {
        int x_;
        int y_;
    } i_;
    int z_;

    X() { }

    X(const X& rhs)
      : i_(rhs.i_), z_(0) // implementation not copied
    { }

    X& operator=(const X& rhs)
    {
        i_ = rhs.i_;
        return *this;
    } 
};
于 2010-11-08T08:57:59.670 に答える
1

std :: swapの複製に本当に悩まされているのなら、実装の詳細以外のすべてを構造体に入れてみませんか?

struct FooBarCore {
  long id;
  double valX;
  double valZ;
  long   valN;
  bool   flag; 
  // ...
};

struct FooBar {
  FooBarCore core_;
  NonCopyable implementation_detail; // cannot and must not be copied
};

次に、のコピー関数でこの構造体にstd::swapを使用できますFooBar

FooBar& operator=(const FooBar &src) {
  FooBarCore temp(src.core_)
  swap(temp,*this.core_);
  return *this;
}
于 2010-11-08T10:21:01.840 に答える
1

さて、この答えへの私のコメントに基づいて、もう一度試してみてください。

コピー可能なクラスでimplementation_detailをラップします。

class ImplementationDetail
{
public:
  ImplementationDetail() {}
  ImplementationDetail(const ImplementationDetail&) {}
  ImplementationDetail& operator=(const ImplementationDetail&) {}

public: // To make the example short
  Uncopyable implementation_detail;
};

このクラスをFooBarで使用します。デフォルトで生成されたFoobarのコピーコンストラクターとコピー代入演算子は正しく機能します。

たぶんそれはUncopyableから派生する可能性があるのでimplementation_detail.implementation_detail、コード全体を取得することはありません。または、コードをimplementation_detailクラスに制御する場合は、空のコピーコンストラクターと空の代入演算子を追加するだけです。

于 2010-11-08T11:37:16.173 に答える
0

コピーコンストラクターがimplementation_detailをコピーする必要がなく、それでも正しい場合(後者は疑わしいですが、今のところそれを想定しましょう)、implementation_detailは不要です。

したがって、解決策は次のようになります。implementation_detailを静的にし、デフォルトで生成されたコピーコンストラクターと代入演算子に依存します。

于 2010-11-08T08:40:36.613 に答える