web-dev-qa-db-ja.com

C ++のプライベートコピーコンストラクタの使用は何ですか

なぜプライベートコピーコンストラクターを定義するのですか?

コピーコンストラクターと代入演算子を非公開にするのはいつよい設計ですか?

クラス内に一意のオブジェクト(ファイル名など)へのポインターまたはハンドルであるメンバーが存在しない場合、プライベートコピーコンストラクターが良いアイデアである他のケースはありますか?

代入演算子にも同じ質問が適用されます。 C++の大部分がオブジェクトのコピーと参照渡しを中心に展開していることを考えると、プライベートコピーコンストラクターを含む優れた設計はありますか?

42
user796530

一部のオブジェクトは、コピーできないまたはコピーできない特定のエンティティを表します。たとえば、アプリケーションで使用されるログファイルを表すオブジェクトのコピーを防止できます。これは、コードのすべての部分で1つのログファイルが使用されるという期待に対応しています。誤ってまたは不適切にコピーされたオブジェクトを使用すると、ログに異常なコンテンツが表示されたり、現在のログサイズのレコードが不正確になったり、新しいログファイル名に「ロール」したり、既存のファイル名を変更しようとする複数の試み(一部失敗)が発生する可能性があります。

別の用途は、仮想関数を介してコピーを強制することです。コンストラクターはvirtualにはできないため、一般的な方法は、コピーコンストラクターへの直接アクセスを防止し、実際の実行時型のコピーを返すvirtual Base* clone()メソッドを提供することです。ポインターポイント。これにより、Base b(derived)が示す誤ったスライスが防止されます。

別の例:コンストラクターで指定されたポインターを単純に削除する非常にシンプルなスマートポインターオブジェクト:参照カウントまたは複数の所有者を処理する他の方法をサポートせず、意図しないstd::auto_ptrスタイルの所有権の移転、コピーコンストラクターの非表示は、使用可能な限られた場合に高速で効率的な、優れた小さなスマートポインターを提供します。それをコピーしようとすることに関するコンパイル時エラーは、プログラマーに「ねえ-本当にやりたいなら共有ポインターに変えてください。

32
Tony Delroy

ユースケースの1つは、-シングルトンパターンです-クラスのインスタンスは1つだけですです。この場合、コンストラクターと代入operator =をprivateにして、複数のオブジェクトを作成する方法がないようにする必要があります。オブジェクトを作成する唯一の方法は、以下に示すようにGetInstance()関数を使用することです。

// An example of singleton pattern
class CMySingleton
{
public:
  static CMySingleton& GetInstance()
  {
    static CMySingleton singleton;
    return singleton;
  }

// Other non-static member functions
private:
  CMySingleton() {}                                  // Private constructor
  ~CMySingleton() {}
  CMySingleton(const CMySingleton&);                 // Prevent copy-construction
  CMySingleton& operator=(const CMySingleton&);      // Prevent assignment
};

int main(int argc, char* argv[])
{
  // create a single instance of the class
  CMySingleton &object = CMySingleton::GetInstance();

  // compile fail due to private constructor
  CMySingleton object1;
  // compile fail due to private copy constructor
  CMySingleton object2(object);
  // compile fail due to private assignment operator
  object1 = object;

  // ..
  return 0;
}
35
Eric Z

非常に悪い例:

class Vehicle : { int wheels; Vehicle(int w) : wheels(w) {} }

class Car : public Vehicle { Engine * engine; public Car(Engine * e) : Vehicle(4), engine(e) }

...

Car c(new Engine());

Car c2(c); // Now both cars share the same engine!

Vehicle v;
v = c; // This doesn't even make any sense; all you have is a Vehicle with 4 wheels but no engine.

車を「コピーする」とはどういう意味ですか? (車は車のモデルですか、それとも車のインスタンスですか?コピーしても車の登録は保持されますか?)

車両を別の車両に割り当てるとはどういう意味ですか?

操作が無意味(または単に実装されていない)の場合、標準的なことは、コピーコンストラクタと代入演算子をプライベートにし、奇妙な動作の代わりに使用するとコンパイルエラーが発生することです。

4
tc.

コピーコンストラクターとコピーの割り当てをプライベートにする一般的な理由は、これらの操作の既定の実装を無効にすることです。ただし、C++ 0xには、そのような目的のための特別な構文= deleteがあります。そのため、C++ 0xでは、コピーctorをプライベートにすることは非常にエキゾチックなケースに制限されているようです。

コピーアクターと割り当ては、むしろ構文上の砂糖です。そのため、このような「プライベートシュガー」は貪欲の症状のようです:)

3
user396672

オブジェクトの内容がポインターや他の参照ではない場合でも、ユーザーがオブジェクトをコピーできないようにすることは有用です。おそらく、クラスには多くのデータが含まれており、コピーは操作の重量が大きすぎます。

1
Nicol Bolas

" virtual constructor idiom "は、プライベートまたは保護されたコピーコンストラクターが必要な重要なケースです。 C++では、この基本クラスから実際に継承されたオブジェクトの基本クラスへのポインターが与えられ、そのコピーを作成したいという問題が発生します。コピーコンストラクターを呼び出すと、継承クラスのコピーコンストラクターは呼び出されませんが、実際には基本クラスのコピーコンストラクターが呼び出されます。

観察する:

class Base {

public:
   Base( const Base & ref ){ std::cout << "Base copy constructor" ; }
};

class Derived : public Base {

public:
   Derived( const Derived & ref ) : Base(ref) { std::cout << "Derived copy constructor"; }
}

Base * obj = new Derived;
Base * obj2 = new Derived(*obj);

上記のコードは出力を生成します:

"Base copy constructor"

これは明らかに、プログラマが望んだ動作ではありません!プログラマは、タイプ「Derived」のオブジェクトをコピーしようとしましたが、代わりにタイプ「Base」のオブジェクトを取得しました!!

この問題は、前述のイディオムを使用して修正されます。このイディオムを使用するように書き直された上記の例を観察します。

class Base {

public:
  virtual Base * clone () const = 0; //this will need to be implemented by derived class

protected:
   Base( const Base & ref ){ std::cout << "Base copy constructor" ; }
};

class Derived : public Base {

public:
  virtual Base * clone () const {

    //call private copy constructor of class "Derived"
    return static_cast<Base *>( new Derived(*this) );
  }

//private copy constructor:
private:
   Derived( const Derived & ref ) : Base(ref) { std::cout << "Derived copy constructor"; }
}

Base * obj = new Derived;
Base * obj2 = obj->clone();

上記のコードは出力を生成します:

"Base copy constructor"
"Derived copy constructor"

つまり、「ベース」タイプではなく、「派生」タイプのオブジェクトで構築されたオブジェクトです。

ご覧のとおり、派生型では、コピーコンストラクターは意図的にプライベートにされました。これは、クローンによって提供される巧妙なインターフェイスを使用するのではなく、プログラマーが誤ってコピーコンストラクターを手動で呼び出そうとすると、APIの設計が悪くなるためです)。別の言い方をすると、直接呼び出し可能なパブリックコピーコンストラクターを使用すると、プログラマはパート1で述べた間違いを犯す可能性があります。この場合、ベストプラクティスは、コピーコンストラクターを非表示にし、メソッド "clone( )」。

0
J T

コピーコンストラクタを使用してクラスのメソッドの一部を実装することもできますが、クラスの外部に公開することは避けてください。それで、あなたはそれをプライベートにします。他の方法と同様。

0
Petar Ivanov