std::auto_ptr

http://www.geocities.jp/ky_webid/cpp/library/026.html

これは以前、テンポラリバッファとしてのvector - (void*)Pないとの中で名前だけ出てきましたね。

テンポラリバッファとしての単一オブジェクトをnewするときに便利といった感じなのでしょうか。

見てみましょう。auto_ptrを使うにはmemoryヘッダが必要となります。

#include <iostream>
#include <memory>
using std::cout; using std::endl;

class CSample {
public:
    CSample(int num) {
        m_num = num;
        cout << "コンストラクタ" << endl;
    }
    ~CSample() {
        cout << "デストラクタ" << endl;
    }
    int getNum () { return m_num; }
private:
    int m_num;
};

std::auto_ptr<CSample> create (int num) {
    std::auto_ptr<CSample> p( new CSample(num) );
    return p;
}

int main () {
    std::auto_ptr<CSample> p = create(100);
    cout << p->getNum() << endl;
    return 0;
}
$ main
コンストラクタ
100
デストラクタ

createの中でCSampleをnewしているのに、main関数に戻ってきてからデストラクタが呼ばれています。

これはつまり、auto_ptrで定義した変数pがcreate関数を抜けてもその時点ではCSampleをdeleteしていない証拠ですね。

一体この動作はどうやって実現しているのでしょうか?

自身は new で確保された訳ではなく、単なるローカル変数です。なので、暗黙のうちにコピーを取り、戻り値で返した後、デストラクタが呼び出されて破棄されます。この中でコピーという動作がポイントです。auto_ptr型の変数を、コピーすると(代入すると)、その auto_ptr が管理しているポインタが、コピー先に移動し、コピー元の方は NULL になります。

なるほど。

コピーコンストラクタや代入演算した呼ばれたときにauto_ptrが管理しているCSampleのポインタを移動させているわけなんですね。

だからcreate関数でのp変数のデストラクタが呼ばれても、その時点ではポインタが移動しているのでCSampleがdeleteされないんですね。

素晴らしいです。

ですが逆に言えば代入した後の、代入元の変数は使用できないということになりますね。

int main () {
    std::auto_ptr<CSample> p1( new CSample(100) );
    cout << p1->getNum() << endl;
    
    // p1をp2に代入すると、p1のCSampleがp2へ移動する
    std::auto_ptr<CSample> p2 = p1;
    
    // よってp1はもう使えない。
    cout << p1->getNum() << endl;
    
    return 0;
}

これを実行するとプログラムが落ちます。

これはこれで微妙ですね。代入しても有効で且つ、一番最後の時にだけCSampleのdeleteが呼ばれれば完璧なんですが。