C++の「3の法則」と「5の法則」をわかりやすく解説
3の法則は、C++プログラミングにおける代表的な経験則の一つです。これは良いプラクティスに関する指針であり、クラスに次のいずれかを明示的に定義する必要があるならば、残りの2つも定義すべきである、というものです。
- コピーコンストラクタ
- 代入演算子
- デストラクタ
なぜ3つすべてが必要なのか
その理由は、これらのいずれかが必要になる場面では、クラスが動的に確保されたリソースを管理している可能性が非常に高く、その管理を正しく行うには他の関数も必要になるからです。
例えば、代入演算子が必要なケースを考えてみましょう。代入処理では、参照されているオブジェクトのコピーを作成することになり、新たにリソースを割り当てることになります。そのリソースを複製するためにコピーコンストラクタが、不要になったリソースを解放するためにデストラクタが必要となるのです。
C++11以降の「5の法則」
5の法則は、C++11でムーブセマンティクスが導入されたことに伴い、3の法則を拡張したものです。こちらもC++におけるリソース管理の指針として用いられ、メモリリークなどの問題を未然に防ぐ効果が期待できます。
5の法則では、次の5つの特殊メンバ関数のうち1つでも自分で実装する必要があるなら、残りの4つについても適切な方針を持つべきだとされています。例えば、Fooというオブジェクトを管理するFooManagerクラスを実装する場合、以下の5つの関数をすべて意識して実装することになります。
1. デストラクタ
マネージャがスコープ外に出るとき、管理していたすべてのリソースを確実に解放しなければなりません。
2. 代入演算子
独自の代入演算子を提供しない場合、コンパイラがデフォルトのものを自動生成します。しかしデフォルトの代入演算子はメンバごとの浅いコピー(シャローコピー)しか行いません。深いコピー(ディープコピー)ではないため、メモリリークや誤った代入などの問題を引き起こす恐れがあります。
3. コピーコンストラクタ
コンパイラが生成するコピーコンストラクタも、FooManagerの全属性をメンバごとにコピーするだけです。そのため、代入演算子の場合と同様の問題が発生します。
4. ムーブコンストラクタ
オブジェクトのコピーは、一時オブジェクトの作成・コピー・破棄という一連の処理を伴うため、コストが高くなりがちです。そこでC++11ではr値参照の概念が導入されました。r値参照は明示的にr値へ束縛することができます。ここでいうr値とは名前を持たないオブジェクト、すなわち一時オブジェクトのことです。ムーブコンストラクタでは、このr値参照を使って、渡された一時オブジェクトからリソースを効率的に引き継ぎます。
5. ムーブ代入演算子
リソースはある時点で1つの所有者だけが持つのが理想です。リソースの所有権は、あるマネージャから別のマネージャへ移転させることができます。このような場面では、ムーブ代入演算子を提供すると非常に有効です。
まとめ
3の法則・5の法則は、どちらも「1つ書いたら全部書く(または明示的に禁止する)」という原則に基づいています。特にモダンC++では、これらの法則に従ってリソース管理を設計することで、安全で効率的なコードを実現できます。さらに詳しく学びたい方は、Feabhas社によるRule of the Big Fiveの解説資料(PDF)がおすすめです。
-
C++で3つの点が同一直線上にあるかどうかを判定するプログラム
3つの異なる座標を持つ点が与えられ、それらの点が同一直線上に並んでいるかどうか(共線性・コリニア)を判定するのが本記事のテーマです。3つの点がすべて同じ直線上に乗っている場合、これらの点は「共線(collinear)」であるといいます。逆に、異なる直線上に配置されている場合は共線ではありません。以下の図は、共線な点と共線でない点の違いを示したものです。入力例と出力例入力1x1 = 1, x2 = 2, x3 = 3, y1 = 1, y2 = 4, y3 = 5出力1no points are not collinear入力2x1 = 1, y1 = 1, x2 = 1, y2 = 4, x3
-
C++の「3つのルール(Rule of Three)」とは?基本とその理由を解説
C++の「3つのルール(Rule of Three)」とは?「3つのルール(Rule of Three)」は、C++プログラミングにおける代表的な経験則のひとつです。これはコーディングの良いプラクティスを示すガイドラインであり、次の3つの特殊メンバ関数のうち、どれか1つでもクラスに明示的に定義する必要がある場合は、残りの2つも定義すべきだというものです。コピーコンストラクタ代入演算子(コピー代入演算子)デストラクタなぜ3つすべてが必要なのかこのルールが存在する理由は、これらの関数のいずれかが必要になる状況では、そのクラスが動的に確保されたリソース(メモリなど)を管理しているケースがほとんどだか