C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++の「3つのルール(Rule of Three)」とは?例外安全なコードを書くための基本を解説

C++の「3つのルール(Rule of Three)」とは

「3つのルール(Rule of Three)」は、例外安全なコードを構築するためのC++の経験則のひとつです。このルールは、例外による不具合を防ぐために、クラスのデフォルトメンバーをどのように扱うべきかを規定しています。

3つのルールは「ビッグスリーの法則(Law of Big Three)」あるいは「The Big Three」としても知られており、次の3つの特殊メンバー関数のうち、いずれか1つでもクラス内で定義する場合は、3つすべてを明示的に定義すべきであると定めています。

  • デストラクタ(destructor)
  • コピーコンストラクタ(copy constructor)
  • コピー代入演算子(copy assignment operator)

なぜ3つすべての定義が必要なのか

これらの3つは、クラスの特殊メンバー関数です。プログラマがいずれも明示的に定義しない場合、コンパイラが暗黙的なバージョンを自動的に生成します。しかし、上記のいずれか1つを明示的に定義した場合、残りの2つの暗黙的なバージョンは不適切な動作をする可能性が高いため、再定義が必要になります。

この問題が発生する理由は、暗黙的に生成されるコンストラクタや代入演算子が、データメンバーをシャローコピー(浅いコピー)するためです。クラスが動的に確保されたリソースを指すポインタを保持している場合には、ディープコピー(深いコピー)が必要となります。

また、デフォルトのデストラクタは不要になったオブジェクトを破棄します。コピーコンストラクタが定義されていない場合、デストラクタは2回実行されます。1回目はコピー先のオブジェクトに対して、2回目はコピー元のオブジェクトに対してです。この二重解放を避けるためにも、明示的な定義が不可欠になります。

問題が発生するコード例

それでは、コピーコンストラクタとコピー代入演算子を定義せず、デストラクタのみが存在する場合の例を見てみましょう。

サンプルコード

#include <stdio.h>
class Numbers{
private:
    int num;
    int* ptr;
public:
    Numbers( int n, int* p ) //コンストラクタ
    {
        num = n;
        ptr = new int[ num ];
    }
    ~Numbers() //デストラクタ
    {
        delete ptr;
        ptr = NULL;
    }
};
int main(){
    int arr[ 4 ] = { 11, 22, 33, 44 };
    Numbers Num1( 4, arr );
    // ここで問題が発生する
    Numbers Num2( Num1 );
    return 0;
}

実行結果

*** Error in `./a.out': double free or corruption (fasttop): 0x0000000001f46c20 ***
Aborted

エラーが発生する原因

このエラーは、プログラムがスコープを抜ける際にデストラクタが2回呼び出されるために発生します。まずNum1に対して削除処理が行われ、次にNum2に対して行われます。デフォルトのコピーコンストラクタはポインタptrのコピーを作成しますが、そのコピー先のために新たなメモリを確保しません。そのため、Num1が削除された後も同じメモリ領域を指すptrが残ってしまい、プログラムがクラッシュします。

まとめ

動的に確保したリソースを管理するクラスでは、デストラクタ、コピーコンストラクタ、コピー代入演算子の3つをセットで明示的に定義することが、例外安全で堅牢なコードを書くための重要な原則となります。このルールを守ることで、二重解放やメモリリークといった深刻なバグを未然に防ぐことができます。

  1. C++の「3の法則」と「5の法則」をわかりやすく解説

    3の法則は、C++プログラミングにおける代表的な経験則の一つです。これは良いプラクティスに関する指針であり、クラスに次のいずれかを明示的に定義する必要があるならば、残りの2つも定義すべきである、というものです。 コピーコンストラクタ 代入演算子 デストラクタ なぜ3つすべてが必要なのか その理由は、これらのいずれかが必要になる場面では、クラスが動的に確保されたリソースを管理している可能性が非常に高く、その管理を正しく行うには他の関数も必要になるからです。 例えば、代入演算子が必要なケースを考えてみましょう。代入処理では、参照されているオブジェクトのコピーを作成することになり、新たにリソース

  2. C++の「3つのルール(Rule of Three)」とは?基本とその理由を解説

    C++の「3つのルール(Rule of Three)」とは?「3つのルール(Rule of Three)」は、C++プログラミングにおける代表的な経験則のひとつです。これはコーディングの良いプラクティスを示すガイドラインであり、次の3つの特殊メンバ関数のうち、どれか1つでもクラスに明示的に定義する必要がある場合は、残りの2つも定義すべきだというものです。コピーコンストラクタ代入演算子(コピー代入演算子)デストラクタなぜ3つすべてが必要なのかこのルールが存在する理由は、これらの関数のいずれかが必要になる状況では、そのクラスが動的に確保されたリソース(メモリなど)を管理しているケースがほとんどだか