デストラクタとコンストラクタ

クラスでは、クラスの名前と同じ名前のメンバ関数を定義することが出来ます。

class Test
{
char* name;
public:
    Test() //コンストラクタ
    {
        name = new char[10];
        strcpy(name, "name");
    }

    ~Test() //デストラクタ
    {
        delete[] name;
    }
};

コンストラクタ(constructor)は 、クラスのインスタンスが作られた直後に自動的に実行されるメンバ関数です。デストラクタ(destructor)は、クラスのインスタンスが消える直前に自動的に実行されるメンバ関数です。

コンストラクタとデストラクタには返り値の型の指定がありません。また、デストラクタは必ず~(チルダ)から始まります。

上の例では、

Test t1;
Test* t2 = new Test;

の様にインスタンスが生成された直後、コンストラクタが呼ばれて、メンバ変数nameに文字列"name"がセットされます。

デストラクタは、t1の場合はスコープから抜ける直前、t2はdeleteで明示的に消されるときに実行されます。

引数を持つコンストラクタ

引数を持つコンストラクタを定義するとオブジェクトのインスタンス化と代入(正しくは初期化)を同時に記述できます。

class Test
{
char* name;
public:
    Test() //コンストラクタ
    {
        name = new char[10];
        strcpy(name, "name");
    }

    Test(char* n)
    {
        name = new char[strlen(n)+1];//nと同じ文字数を確保
        strcpy(name, n);//nの内容をコピー
    }

    ~Test() //デストラクタ
    {
        delete[] name;
    }
};

とすると

Test tt = "myName"; //オブジェクトのインスタンス化と代入を同時に行う

の様に記述できます。

コピーコンストラクタ

コンストラクタは任意の引数を持つものを定義できますが、特に自分と同じクラスの参照を一つだけ引数に取るクラスをコピーコンストラクタと呼びます。

class Test
{
char* name;
public:
    Test() //コンストラクタ
    {
        name = new char[10];
        strcpy(name, "name");
    }

    Test(Test& t)
    {
        name = new char[strlen(t.name)+1];//t.nameと同じ文字数を確保
        strcpy(name, t.name);//t.nameの内容をコピー
    }

    Test(char* n)
    {
        name = new char[strlen(n)+1];//nと同じ文字数を確保
       
strcpy(name, n);//nの内容をコピー
    }

    ~Test() //デストラクタ
    {
        delete[] name;
    }
};

とすると

Test tt1 = "myName"; //オブジェクトのインスタンス化と代入を同時に行う
Test tt2 = tt1; //tt1の内容をtt2コピーする

の様に記述できます。

コンストラクタとoperator=の違い

Test tt = "myName";

Test tt;
tt = "myName";

では動作が異なることに注意してください。前者は、コンストラクタが呼ばれますが、後者はoperator=が呼ばれます。後者を正しく動作させるためには以下の様にoperator=の定義をクラスTestに追加します。

class Test
{
char* name;
public:
    Test() //コンストラクタ
    {
        name = new char[10];
        strcpy(name, "name");
    }

    Test(Test& t) //コピーコンストラクタ
    {
        name = new char[strlen(t.name)+1];//t.nameと同じ文字数を確保
        strcpy(name, t.name);//t.nameの内容をコピー
    }

    Test(char* n)
    {
        name = new char[strlen(n)+1];//nと同じ文字数を確保
        strcpy(name, n);//nの内容をコピー
    }

    Test& operator=(char* n)
    {
        name = new char[strlen(n)+1];//nと同じ文字数を確保
        strcpy(name, n);//nの内容をコピー
        return *this;
    }   

    ~Test() //デストラクタ
    {
        delete[] name;
    }
};

operator=が定義されるとこのクラスは整理できます。どのコンストラクタもやっていることはoperator=と同じなので置き換えましょう。

class Test
{
char* name;
public:
    Test() //コンストラクタ
    {
        *this = "test";//定義したoperator=を使う
    }

    Test(Test& t) //コピーコンストラクタ
    {
        *this = t.name;//定義したoperator=を使う
    }

    Test(char* n)
    {
        *this = n;//定義したoperator=を使う
    }

    Test& operator=(char* n)
    {
        name = new char[strlen(n)+1];//nと同じ文字数を確保
        strcpy(name, n);//nの内容をコピー
        return *this;
    }   

    ~Test() //デストラクタ
    {
        delete[] name;
    }
};

コンストラクタの必要性

CやC++では、変数を宣言しただけでは値が決まりません。これはクラスのメンバ変数も同じです。コンストラクタはインスタンスが作成されたときに実行されるのでメンバ変数の初期値を設定する ために使えます。

デストラクタの必要性

上の例のTestクラスの様にメンバ変数nameにnewで作ったオブジェクトを代入すると、Testのインスタンスが消えてもnameの内容である"name"はいつまでもメモリ上に残ります。 上の例の様にデストラクタでそのテキストを消すことでメモリを綺麗にすることが出来ます。しかもTestクラスを使う人は消し忘れを気にする必要がありません。

コンストラクタとデストラクタを上手く定義することでプログラムは綺麗で簡単になります。

次へ進みます。