RAII

詳しい文脈は忘れてしまったけど、読書会で「C++はデストラクタが弱い」的な話題があがった。
その場はRAII*1使えばいいよ、とだけ言って概要のみ説明しただけだったので補足を。


まずは、RAIIの実例を知ってもらう為に、簡単なサンプルの紹介から。

void write_to_file(const std::vector<std::string>& vec)
{
    std::ofstream fout("out.txt");
    std::copy(vec.begin(), vec.end(), std::ostream_iterator<std::string>(fout, "\n"));
}

このプログラムでは、ファイルストリームのオープンもクローズも明示的に行っていないにもかかわらず、どちらも正しく行われる。
これは、コンストラクタ内部でオープンを行い、デストラクタ内部でクローズを行うことで実現されている。
この「コンストラクタでリソースを取得し、デストラクタで返却する」というイディオムをC++ではRAIIと呼んでいる。


擬似的なコード*2は以下のようになる。

class ofstream
{
    FILE* fp;
    // コピーはいろいろ面倒になるので禁止しておくのが無難
    ofstream(const ofstream&);
    ofstream& operator=(const ofstream&);
    // newも禁止しておきたければ
    void* operator new(std::size_t);
    void* operator new[](std::size_t);
public:
    ofstream(const char* path)
    {
        // コンストラクタ内部でオープンして
        fp = std::fopen(path, "w");
    }
    
    ~ofstream()
    {
        // デストラクタ内部でクローズする
        std::fclose(fp);
    }
    
    ...
};


GCなんて所詮メモリという一リソースの解放を手伝ってくれるだけに過ぎないけど、C++のデストラクタはメモリ以外のリソースの解放を自動化してくれるということですね。
RAIIを知ってしまうと、むしろC++のデストラクタがすばらしく見えてくるはず!

*1:Resource Acquisition Is Initialization

*2:実際にはofstreamなんてクラスはない