Say I've got a class where the sole data member is something like std::string
or std::vector
. Do I need to provide a Copy Constructor, Destructor and Assignment Operator?
9 に答える
クラスのデータメンバーとしてベクトル/文字列オブジェクトのみが含まれている場合は、これらを実装する必要はありません。C ++ STLクラス(ベクトル、文字列など)には、独自のコピーctor、オーバーロードされた代入演算子、およびデストラクタがあります。
ただし、クラスがコンストラクターで動的にメモリを割り当てる場合、単純な浅いコピーは問題を引き起こします。その場合、copy ctor、オーバーロードされた代入演算子、およびデストラクタを実装する必要があります。
通常の経験則によると、それらの1つが必要な場合は、すべてが必要です。
ただし、すべてのクラスがそれらを必要とするわけではありません。クラスにリソース(特にメモリ)がない場合は、リソースがなくても問題ありません。たとえば、単一string
またはvector
構成要素を持つクラスは、特別なコピー動作が必要でない限り、実際にはそれらを必要としません(デフォルトではメンバーをコピーするだけです)。
デフォルトのコピーコンストラクターは、値で宣言されている場合、ベクトルをコピーします。ベクターにポインタを格納した場合は注意してください。そのような場合は、メモリリークや複数の削除を回避するために、コピー/割り当て/破棄に特定の動作を提供する必要があります。
独自の Big Three を作成する必要があるケースがいくつか考えられます。すべての標準コンテナは、それ自体をコピーおよび破棄する方法を知っているため、必ずしもそれらを作成する必要はありません。いつ行うかを知る方法は次のとおりです。
私のクラスはリソースを所有していますか?
ポインターのデフォルトのコピー セマンティクスは、ポインターが指すものではなく、ポインターの値をコピーすることです。何かをディープ コピーする必要がある場合は、それが標準コンテナー内に格納されている場合でも、独自のコピー コンストラクターと代入演算子を記述する必要があります。また、これらのリソースを適切に解放するには、独自のデストラクタを作成する必要があります。
誰かが私のクラスを継承する可能性はありますか?
基本クラスにはデストラクタが必要です。 Herb Sutterは、それらをどうしたいかによって、public
and virtual
(最も一般的なケース) またはand 非仮想のいずれかにすることを推奨しています。protected
コンパイラによって生成されたデストラクタはパブリックで非仮想であるため、コードが含まれていなくても、独自に作成する必要があります。(注: これは、コピー コンストラクターまたは代入演算子を作成する必要があることを意味するものではありません。 )
ユーザーが自分のクラスのオブジェクトをコピーできないようにする必要がありますか?
ユーザーにオブジェクトをコピーさせたくない場合 (コストがかかりすぎる可能性があります)、コピー コンストラクターと代入演算子を宣言するprotected
必要がありますprivate
。必要でない限り、それらを実装する必要はありません。(注: これは、デストラクタを書かなければならないという意味ではありません。 )
結論:
最も重要なことは、コンパイラによって生成されたコピー コンストラクター、代入演算子、およびデストラクターが何を行うかを理解することです。それらを恐れる必要はありませんが、それらについて考え、その行動がクラスに適しているかどうかを判断する必要があります。
いいえ、コンパイラがこれらの関数を自動生成することを許可しない理由はいくつかあります。
私の経験では、それらを自分で定義し、クラスを変更するときにそれらが維持されるようにする習慣を身につけるのが常に最善です。最初に、特定の ctor または dtor が呼び出されたときにブレークポイントを配置したい場合があります。また、それらを定義しないと、コンパイラーがメンバー ctor および dtor へのインライン呼び出しを生成するため、コードが肥大化する可能性があります (Scott Meyers がこれに関するセクションを持っています)。
また、デフォルトのコピー ctors と割り当てを禁止したい場合もあります。たとえば、非常に大きなデータ ブロックを格納して操作するアプリケーションがあります。数百万の 3D ポイントを保持する STL ベクターに相当するものを日常的に使用していますが、これらのコンテナーをコピー構築できるようにすると大変なことになります。そのため、ctor および代入演算子は非公開として宣言され、定義されていません。誰かが書いたらそのように
class myClass {
void doSomething(const bigDataContainer data); // not should be passed by reference
}
その後、コンパイラ エラーが発生します。私たちの経験では、明示的に become() または clone() メソッドを使用すると、エラーが発生しにくくなります。
したがって、自動生成されたコンパイラ関数を避けるべき多くの理由があります。
これらのコンテナには「copyconstructible」要素が必要です。コピーコンストラクタを指定しない場合は、クラスメンバーから推測してクラスのデフォルトのコピーコンストラクタを呼び出します(浅いコピー)。
デフォルトのコピーコンストラクターに関する簡単な説明はここにあります:http://www.fredosaurus.com/notes-cpp/oop-condestructors/copyconstructors.html
デストラクタの場合はそうです。デストラクタを提供しない場合、コンテナはデストラクタまたはデフォルトのクラスデストラクタにアクセスできる必要があります(つまり、デストラクタをプライベートとして宣言すると機能しません)。
ディープコピーを必要とするクラスがある場合は、それらを定義する必要があります。
具体的には、ポインタまたは参照を含むクラスには、次のようなものを含める必要があります。
class foo {
private:
int a,b;
bar *c;
}
主観的には、コンパイラで生成されたバージョンによって提供されるデフォルトの動作が期待/期待どおりでない可能性があるため、常にそれらを定義すると思います。
些細なコンストラクタ/デストラクタなどが問題なく機能するため、文字列やベクトルには使用できません。
クラスに他のデータへのポインタがあり、ディープコピーが必要な場合、またはクラスがリソースの割り当てを解除する必要がある場合、または特別な方法でコピーする必要がある場合。
必要に応じて提供する必要があります。またはあなたのクラスの可能なユーザー。デストラクタは常に必須であり、コピー コンストラクタと代入演算子はコンパイラによって自動的に作成されます。(少なくとも MSVC)