2011-07-25 7 views
36

なぜプライベートコピーコンストラクタを定義するのですか?C++でのプライベートコピーコンストラクタの使用方法

コピーコンストラクタと代入演算子をプライベートなデザインにするのはいつですか?

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

割り当てオペレータにも同じ質問が適用されます。 C++の大半がオブジェクトのコピーと参照渡しを中心にしているので、プライベートコピーコンストラクタを含む良いデザインはありますか?

+0

割り当てコンストラクタのようなものはありません、移動コンストラクタを意味するのですか? – Praetorian

+0

それはtypoだった...今それを修正しました...代入演算子 –

+0

彼は主な理由私はその質問を尋ねています私はC++の本を読んでいて、C++をすべて自分で学習しています...そして私が読んでいる本 Lippman 、Lajoie C++ PrimerとStroustrupなどは、このようなアプローチが必要とされる現実の例を十分には与えていません。 もちろん、徹底的なリストを提供することはできません... しかし、そのようなアプローチが有用ないくつかのケース(例:@tcによる車の例のように) と私はすでに述べていますポインタまたはファイルのような一意のオブジェクトとの関連付け....... –

答えて

27

一部のオブジェクトは、コピーできない、またはコピーしてはいけない特定のエンティティを表します。たとえば、アプリケーションで使用されるログファイルを表すオブジェクトのコピーを防止することができます。これは、コードのすべての部分で単一のログファイルが使用されることに対応します。誤ってまたは不適切にコピーされたオブジェクトを使用すると、ログ内に順序外のコンテンツが表示されたり、現在のログサイズが不正確に記録されたり、新しいログファイル名に複数回試行(失敗)したり、既存のログファイル名を変更することがあります。

もう1つの使用法は、仮想関数を介してコピーを強制することです。コンストラクタをvirtualにすることはできないので、一般的な方法は、コピーコンストラクタへの直接アクセスを防ぎ、ポインタが指す実際の実行時型のコピーを返すメソッドを提供することです。これにより、Base b(derived)が偶発的にスライスすることを防ぎます。

もう一つの例:コンストラクタで与えられたポインタを単純に削除する死んだ単純なスマートポインタオブジェクト:参照カウントや複数の所有者を扱う他の方法をサポートしておらず、リスクを持ちたくない場合厄介な意図していないstd::auto_ptr所有権のスタイルの転送は、コピーコンストラクタを隠すだけで、使用可能な限られた場合には高速かつ効率的な小さなスマートポインタが得られます。それをコピーしようとすることについてのコンパイル時エラーは、プログラマーに "ちょっと - 本当にやりたいのであれば私を共有ポインタに変更するか、そうでなければ元に戻す!"と尋ねるでしょう。

+0

と一致すると、コピーを禁止することができます.3つのルールに従う必要がありますか?つまり、代入演算子とデストラクタが必要ですか? – ThunderWiring

+0

@ThunderWiring:要するに、代入演算子を削除するのは良い考えですが、デストラクターはまだ作成が許可されているインスタンスによって必要です。代入の場合、自己割り当て(唯一可能な代入)が安全であるシングルトンのような重要なケースはありませんが、あまり役に立ちませんので、混乱を避けるため代入演算子を切り捨てる価値があります。 –

0

コピーコンストラクタを使用してクラスのメソッドのいくつかを実装し、クラスの外部に公開することはできません。それであなたはそれを秘密にします。他の方法と同様。

+0

これは私の質問です。 異なる理由があるかもしれません... いくつかの例を提供できれば助かります。 –

33

つのユースケースは、のみクラスのうちの正確に1つのインスタンスが存在することができるシングルトンパターンあります。この場合、複数のオブジェクトを作成する方法がないように、コンストラクタと代入演算子= 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; 
} 
+2

ohk ...これは1つのケースです...コンストラクタでさえもこのケースでプライベートになります.... –

1

オブジェクトの内容がポインタまたは他の参照でない場合でも、オブジェクトをコピーすることを防ぐことは、依然として有効です。おそらく、クラスには大量のデータが含まれており、コピーは操作の重すぎます。

+0

はい、私は理解しています...しかし、それはプログラマーの権利に残す必要がありますか?プログラマがコピーすることを義務づけるのではなく、参照によってオブジェクトにアクセスすることができます。 –

+0

@Jayesh:良いAPIは誤用することができません。参照でオブジェクトにアクセスすることはできますが、コピーすることを許可する必要はありません。 –

+0

私はあなたが正しいと思います... この特定のケースで私の友人が私に納得しました... –

4

非常に悪い例:それは "コピー" 車に

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. 

何を意味するのでしょうか? (車は車のモデルですか、車のインスタンスですか?それをコピーしても車の登録は維持されますか?)

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

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

+0

車の問題で...車として車を暴露しようとすると、エンジンはありませんその車のオブジェクトの中のその車に対応しています。そういう意味では...あなたは車の車輪だけを取り、そこからオブジェクトを作ることを定義する車用の特殊なコンストラクタを構築する必要があります。問題は、コピーコンストラクタではなくクラスの設計です。 –

+2

@Jayesh Badwaik:あなたは現実の例を求めていました。現実のように、そこにはやや醜いデザインがかなり一般的です。特定の法律や何十年もの官僚制からインスパイアされたコードを見ることさえしないでください。醜さをローカルに保つことがさらに役立ちます。 "このクラスはちょっと変わっていますが、フォームF11-M3付録A" – MSalters

0

"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"型のオブジェクトを戻しました!!

上記のイディオムを使用して問題を修正します。すなわち

"Base copy constructor" 
"Derived copy constructor" 

、所望のタイプの中で構築された」オブジェクト:上記のコードは出力を生成する

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(); 

:上記の記述された例を観察し、このイディオムを使用するように書き換え「派生型」であり、「ベース」型ではありません。

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

+0

C++で暗黙のダウンキャストがないため、最初の例はコンパイルされません(Base * obj2 = new Derived(* obj)はエラーを生成します。* objは暗黙的にDerived&に変換されません)。だから、あなたは仮想コンストラクタイディオム – user396672

3

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

コピーctorsと代入は構文的な砂糖です。そのような "私的砂糖"は欲望の症状のようです:)

+0

:)で解決しようとする問題は何かわかりません。情報のおかげで..... 私はそれを見てみましょう... 個人的に..私はその使用を感じたコピーコンストラクタの最小限に保つ必要がありますし、確かに完全なシステム設計(効果はできるだけ限られている必要があります)に浸透してはなりません....可能な限り... 今私は高いと対処しているパフォーマンス計算コード... だから私はそれは大丈夫だと思います... しかし、私はそれがなくても良いと感じています..... –

関連する問題