先来一段代码:
#include <memory>
#include <atlbase.h>
class A
{
public:
A() : ma(0) {}
void print() {
ATLTRACE(_T("%d"), ma);
}
private:
int ma;
};
int _tmain(int argc, _TCHAR* argv[])
{
std::auto_ptr<A> aPtr = new A();
aPtr->print();
return 0;
}
Debug运行这段代码的时候,直接报错,提示aPtr不可以解引用。
看了好几遍,感觉好像没什么问题,找不到头绪。 没办法,一步步的跟进去调试看到底在哪里出的事。
跟到auto_ptr的构造函数,才发现它调用的构造函数不对,上述代码调用的构造函数如下:
auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()
{ // construct by assuming pointer from _Right auto_ptr_ref
_Ty **_Pptr = (_Ty **)_Right._Ref;
_Ty *_Ptr = *_Pptr;
*_Pptr = 0; // release old
_Myptr = _Ptr; // reset this
}
auto_ptr_ref是一个proxy类,它接受void*类型的指针参数,代码如下:
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
};
这说明,new运算符返回的指针类型是个void*,问题极有可能出上述auto_ptr构造函数里面。
auto_ptr_ref的_Ref持有了new出对象的指针,而在上述auto_ptr构造函数中,把它当作了A**来使用,并解引用了一次来获取A*。
很明显,这一次解引用导致auto_ptr中指针取到的值是0。
同时,下面的代码也没有调用到正确的构造函数,它调用的构造函数跟上面的是一样的:
A a;
std::auto_ptr<A> aPtr = &a;
aPtr->print();
auto_ptr的指针转换构造函数带了explicit关键字,除非写成 aPtr(&a)。可以用下面的例子说明这两者之间的区别:
class B
{
public:
explicit B(A* pa) : ma(pa) { }
B(void* p) : ma(0) {}
private:
A* ma;
};
int _tmain(int argc, _TCHAR* argv[])
{
A a;
B b = &a;
return 0;
}
上面调用的是B(void*)这个构造函数,如果把explicit去掉,调用的是B(A*)这个构造函数。