VS2005のauto_ptrというかauto_ptr_refのバグ
以下のauto_ptrを使ったコード、VC6だとエラーになるんだけどVS2005だとコンパイルが通る。が、main()を抜ける時に走る~A()で落ちる。
#include<memory> #include<iostream> using namespace std ; class A { public : A( void ){ cout << "ctor" << endl ; } virtual ~A( void ){ cout << "dtor" << endl ; } } ; void main( void ) { cout << "auto_ptr" << endl ; auto_ptr<A> ap_A = new A() ; // (A) プログラムが落ちる。 // auto_ptr<A> ap_A( new A() ) ; // (B) こっちはVC6/VS2005で正常に動作する。 }
auto_ptr<A>にA *を引数にとるコピーコンストラクタが存在しないため、本来はエラーにならなければならない(たぶん)はずだが、下記にしめすauto_ptr_refが絡んできて暗黙の型変換が行われエラーにならない。具体的には、auto_ptr_refクラスのコンストラクタ(auto_ptr_ref<A>::auto_ptr_ref(void *))によって、(A)の箇所が次のようになる。
-
- auto_ptr<A> ap_A = auto_ptr_ref( new A() )
template<class _Ty> struct auto_ptr_ref { // proxy reference for auto_ptr copying auto_ptr_ref(void *_Right) : _Ref(_Right) { // construct from generic pointer to auto_ptr ptr } void *_Ref; // generic pointer to auto_ptr ptr };
この暗黙の型変換により、下記(D)にしめすauto_ptr_ref<A>を引数にとるコピーコンストラクタが呼び出される。
なお、正常に動作する上記(B)の場合は(C)のコンストラクタが呼び出される。
explicit auto_ptr(_Ty *_Ptr = 0) _THROW0() // (C) : _Myptr(_Ptr) { // construct from object pointer } auto_ptr(auto_ptr<_Ty>& _Right) _THROW0() : _Myptr(_Right.release()) { // construct by assuming pointer from _Right auto_ptr } auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0() // (D) { // construct by assuming pointer from _Right auto_ptr_ref _Ty **_Pptr = (_Ty **)_Right._Ref; // (D-1) _Ty *_Ptr = *_Pptr; // (D-2) *_Pptr = 0; // release old // (D-3) _Myptr = _Ptr; // reset this // (D-4) }
この暗黙の型変換はたぶん意図されていないと思われるので、(D)のコピーコンストラクタ内の動作がおかしくなる。
(D-1)
まず(D-1)のコードで_Right._Refはnew A()の結果、ようするにA*が格納されているのに_Ty **_Pptr、すなわちA **にキャストされている。結果として_PptrにはAのインスタンスへのポインタがダブルポインタとして格納される。
(D-2)
(D-2)の*_Pptrは*((A **)(new A())) すなわち、Aのインスタンスの先頭4バイトが指す領域(たぶん今回はvftableが指す領域)へのポインタであるので、_Ptrにはvftableが指す領域へのポインタが設定される。注意すべきはこれがA*であるとコンパイラが認識しているという事。
(D-3)
そして、(D-3)で*_Pptrに0を代入しているが、*_PptrはAのインスタンスそのものであるため、Aのインスタンスの先頭に0を書き込んでいる。結果としてAのインスタンスの先頭にあるvftableが破壊される。
(D-4)
最後の(D-4)で、本来A*を指す_Myptrに(D-2)で設定されたvftableが指す領域へのポインタが格納される。
ようするに、auto_ptr<A>::_Myptrが変な所を指していることになる。よって、auto_ptr<A>のデストラクタがdelete _Myptrを実行したところで落ちる。
結論
結局の所、VS2005で導入されたauto_ptr_refクラスのせいで、エラーになるべき所がエラーにならないのが原因みたい。それにしても、auto_ptr_refは何がしたくて導入されたんだろう。