コンストラクタとデストラクタの必要性って何よ

って、知り合いから質問が来たので、


「関数オブジェクトの引数が渡せないだろ情弱が!」


などと適当に返したのですが、自分なりのまとめをば。
例によって、完全にチラシの裏なのでどうでもいい人は適当にスルーしてください。
中の人もそんなに詳しい訳じゃない当たり障りのない範囲で。

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

そもそもコンストラクタとデストラクタって何よ!って話。
オブジェクトが生成される時とオブジェクトが破棄された時に呼ばれる処理の事である。
C++だとこんな感じで呼ばれる。

#include <iostream>

struct object{
    object(){
        std::cout << "constructor" << std::endl;
    }
    ~object(){
        std::cout << "destructor" << std::endl;
    }
};

int
main(){
    std::cout << "WRYYYYYYYYYYYY" << std::endl;
    
    {
    object    obj;    // オブジェクトが生成された時にコンストラクタが呼ばれ
    object*    buf = new object; // new でメモリが確保された時も呼ばれる
    
    std::cout << "貧弱!貧弱ゥ!" << std::endl;
    
    delete buf;        // delete で解放した時もデストラクタが呼ばれる
    }    // スコープを抜けてオブジェクトが破棄される時にデストラクタが呼ばれる

    std::cout << "無駄無駄無駄無駄無駄無駄" << std::endl;

    return 0;
}


[出力]

WRYYYYYYYYYYYY
constructor
貧弱!貧弱ゥ!
destructor
無駄無駄無駄無駄無駄無駄


こんな感じで、オブジェクトが生成される時や破棄される時に呼ばれる。
別の言い方をすれば、オブジェクトが生成される時と破棄される時に処理する事を保障する機構とも言える。

2.コンストラクタとデストラクタって何に使うの

ざっくり言うと、変数の初期化や後始末をするときに使用します。

struct object{
    object(){
        value = -1;            // -1 が初期値の設定とか
        array = new int[10];
        memset(array, 0, sizeof(int)*10);
    }
    ~object(){
        delete array;
    }
private:
    int        value;
    int*    array;
};


こんな感じで変数に対して初期値を割り当てたり、メモリの確保したり解放したりします。

3.初期化や後始末はメソッドにして呼び出せば問題ないんじゃなイカ

つまり、initialize() や finalize() みたいなメソッドを作成して、それを呼び出せば問題ないよねって話。

#include <iostream>

struct object{
    object(){
        // コンストラクタは空
    }
    ~object(){
        // デストラクタは空
    }

    // こっちを呼んでね☆
    // お兄さんとの約束だゾ!
    void initialize(){
        value = 0;
        array = new int[10];
        memset(array, 0, sizeof(int)*10);
    }
    void finalize(){
        delete array;
    }

private:
    int        value;
    int*    array;
};

int
main(){
    
    object obj;
    obj.initialize();    // オブジェクトを作ったときはこれを呼ぶでゲソ!

    // ごにょごにょ

    obj.finalize();        // 終わるときは終了処理をするんだよ
    
    return 0;
}


まぁ見て分かるとおりいちいちメソッドを呼ばなければならないので非常にめんどくさいですね。
そもそも使われる側から initialize() や finalize() が呼ばれる保証がどこにも無いので簡単にメモリ破壊やメモリリークが起きそうです。

void
hogehoge(){
    object obj;
    obj.initialize();
    
    // ごにょごにょ
    
    obj.initialize();  // 2回目の呼出とか…。
    
}    // finalize() を呼ばなくて大丈夫か


こんな感じで諸悪の根源が呼ばれます。

4.結論

コンストラクタとデストラクタは、安全性の面から見て、初期化と後始末を保証するために必要です。
initialize() や finalize() 等の作成側のお約束なんて使う側からすれば知りませんからね。


まぁ initialize() や finalize() が全く必要ないのかといえばそうでもないのですが。
実際に重い処理を行う場合や明示的に呼び出しが必要な時は、メソッドとして扱ったほうが安全な場合がありますが、ちょっと意味が違ってくるので割愛します。
(そこら辺は設計の問題じゃないだろうか)
他にもコピーコンストラクタやコンストラクタ/デストラクタ内での仮想関数を呼び出してはいけない等ありますが、やぶ蛇に突っ込みたくないのでこの辺にしておきます。


簡単に私的でまとめてみたけどこんな感じであってるでゲソか。
殆ど肯定的な部分ばかりで否定的な部分がないでゲソね。
もっと詳しく知りたい人は、EffectiveC++ 辺りでも読めばいいんじゃなイカ
と、適当な事を言って〆たいと思います。