質問: shared_ptrからpimplのデストラクタ呼び出し pimplイディオムの勉強をするために以下のようなプログラムを作りました。 //mysharedptr.h template class mysharedptr { tnt *mptr; public: mysharedptr(tnt *ptr) : mptr(ptr) {} ~mysharedptr() { delete mptr; } //(1) }; //foo.h class foo { struct stimpl; mysharedptr mimpl; //(2) public: foo(); }; //foo.cpp foo::stimpl { ~stimpl() { cout << "xxx" << endl; } //(3) }; foo::foo() : mimpl(new stimpl) {} //main.cpp int main() { foo foo; } これをコンパイルすると、(1)の所でtntのデストラクタが見つからない旨のワーニングが出て、mainを実行するとstimplのデストラクタが呼ばれずに(3)の出力は出ません。ただ、(2)をboost::shard_ptrに変えるとワーニングも出ないし(3)の出力もされます。(2)の箇所でstimplが宣言だけなのは両者とも同じだと思うのですが、なぜboostはワーニングが出ないのでしょうか。また、mysharedptrでもワーニングを出さないようにすることはできるのでしょうか。もちろん、スマートポインタを実装するよりもboostを使用した方が良いとは思うのですが、何でboostはうまくいくのか不思議に思い、質問させていただきました。
回答: これは、pimplの有名な問題でして、 本来の、正しい解決法は、 foo.hに、fooクラスのデストラクタの宣言 ~foo(); を明示的に書いて、 foo.cppの一番最後(foo:stimplの定義が終わったあとに) foo:~foo() {} とfooの(空の)デストラクタを定義することです。 boost::scoped_ptr や boost::shared_ptrでは、 boost::checked_deleteてやつが使われていて、不完全なクラスのdeleteが起きないようにチェックしてくれます。 ------ boost::shared_ptrでうまくいってしまう理由はよくわかりません。 pimpleのポインタを持つだけなら、boost::scoped_ptrで十分でして、boost::shared_ptrはオーバースペックなわけですが、 fooの明示的なデストラクタ宣言がない状態で、boost::shared_ptrを使うと、checked_delete でちゃんとコンパイルエラーが起きます。 おそらくshared_ptrは、参照カウンタを作るために、コンパイラがfooクラスの実体が作られるのが遅くなって、コンパイラがfooクラスの実体を作るときにfoo::stimplクラスを知っている状態になっているからでしょう。 ただし、shared_ptrでうまくいっているのは、あくまで、たまたまで、コンパイラを変えたりすればうまく行くとは限らないはずです。 正しい解決法は、上に書いたように、foo に(空の)デストラクタを定義することです。