1 auto_ptr使用介绍
auto_ptr 是c++ 98定义的智能指针模板,其定义了管理指针的对象,可以将new 获得(直接或间接)的地址赋给这种对象。当对象过期时,其析构函数将使用delete 来释放内存!
使用建议:
- 尽可能不要将auto_ptr 变量定义为全局变量或指针。
- 除非自己知道后果,不要把auto_ptr 智能指针赋值给同类型的另外一个智能指针。
- C++11 后auto_ptr 已经被“抛弃”,已使用unique_ptr替代!
示例代码如下:
#include <iostream>
#include <string>
#include <exception>
#include <memory>
using namespace std;
//auto_ptr< Test> t(new Test()); //忠告1: 智能指针不要定义为全局变量
class Test
{
public:
Test() {
cout << "Test is construct" << endl;
debug = 1;
}
~Test() { cout << "Test is destruct" << endl; }
int getDebug() {
return debug;
}
private:
int debug;
};
//用 法: auto_ptr<类型> 变量名(new 类型)
void memory_leak_demo1() {
auto_ptr< Test> t(new Test());
//忠告3: 除非自己知道后果,不要把auto_ptr 智能指针赋值给同类型的另外一个智能指针
//auto_ptr< Test> t1;
//t1 = t;
//auto_ptr<Test>* tp = new auto_ptr<Test>(new Test()); //忠告2: 不要定义指向智能指针对象的指针变量
//在使用智能指针访问对象时,使用方式和普通指针一样
cout<< "-> debug: "<<t->getDebug()<< endl;
cout << "* debug: " << (*t).getDebug() << endl;
//Test* tmp = t.get();
//cout << "get debug: " << tmp->getDebug() << endl;
//release 取消指针指针对动态内存的托管,之前分配的内存必须手动释放
//Test* tmp = t.release();
//delete tmp;
//reset 重置智能指针托管的内存地址,如果地址不一致,原来的会被析构掉
//t.reset();
t.reset(new Test());
if(0){
Test* t1 = new Test();
t1->getDebug();
}
return;
}
int memory_leak_demo2() {
//Test* t = new Test();
auto_ptr< Test> t(new Test());
/***********************************************
* 程序执行一段复杂的逻辑,假设尝试从一个必须存在
* 的文件中读取某些数据,而文件此时不存在
************************************************/
{
throw exception("文件不存在");
}
//delete t;
return 0;
}
int main()
{
memory_leak_demo1();
/*try {
memory_leak_demo2();
}
catch (exception e) {
cout << "catch exception: " << e.what() << endl;
}*/
system("pause");
return 0;
}
auto_ptr的源码如下,虽然不能完全看懂还是贴在这里:
#if _HAS_AUTO_PTR_ETC
// CLASS TEMPLATE auto_ptr
template <class _Ty>
class auto_ptr;
template <class _Ty>
struct auto_ptr_ref { // proxy reference for auto_ptr copying
explicit auto_ptr_ref(_Ty* _Right) : _Ref(_Right) {}
_Ty* _Ref; // generic pointer to auto_ptr ptr
};
template <class _Ty>
class auto_ptr { // wrap an object pointer to ensure destruction
public:
using element_type = _Ty;
explicit auto_ptr(_Ty* _Ptr = nullptr) noexcept : _Myptr(_Ptr) {}
auto_ptr(auto_ptr& _Right) noexcept : _Myptr(_Right.release()) {}
auto_ptr(auto_ptr_ref<_Ty> _Right) noexcept {
_Ty* _Ptr = _Right._Ref;
_Right._Ref = nullptr; // release old
_Myptr = _Ptr; // reset this
}
template <class _Other>
operator auto_ptr<_Other>() noexcept { // convert to compatible auto_ptr
return auto_ptr<_Other>(*this);
}
template <class _Other>
operator auto_ptr_ref<_Other>() noexcept { // convert to compatible auto_ptr_ref
_Other* _Cvtptr = _Myptr; // test implicit conversion
auto_ptr_ref<_Other> _Ans(_Cvtptr);
_Myptr = nullptr; // pass ownership to auto_ptr_ref
return _Ans;
}
template <class _Other>
auto_ptr& operator=(auto_ptr<_Other>& _Right) noexcept {
reset(_Right.release());
return *this;
}
template <class _Other>
auto_ptr(auto_ptr<_Other>& _Right) noexcept : _Myptr(_Right.release()) {}
auto_ptr& operator=(auto_ptr& _Right) noexcept {
reset(_Right.release());
return *this;
}
auto_ptr& operator=(auto_ptr_ref<_Ty> _Right) noexcept {
_Ty* _Ptr = _Right._Ref;
_Right._Ref = 0; // release old
reset(_Ptr); // set new
return *this;
}
~auto_ptr() noexcept {
delete _Myptr;
}
_NODISCARD _Ty& operator*() const noexcept {
#if _ITERATOR_DEBUG_LEVEL == 2
_STL_VERIFY(_Myptr, "auto_ptr not dereferencable");
#endif // _ITERATOR_DEBUG_LEVEL == 2
return *get();
}
_NODISCARD _Ty* operator->() const noexcept {
#if _ITERATOR_DEBUG_LEVEL == 2
_STL_VERIFY(_Myptr, "auto_ptr not dereferencable");
#endif // _ITERATOR_DEBUG_LEVEL == 2
return get();
}
_NODISCARD _Ty* get() const noexcept {
return _Myptr;
}
_Ty* release() noexcept {
_Ty* _Tmp = _Myptr;
_Myptr = nullptr;
return _Tmp;
}
void reset(_Ty* _Ptr = nullptr) noexcept { // destroy designated object and store new pointer
if (_Ptr != _Myptr) {
delete _Myptr;
}
_Myptr = _Ptr;
}
private:
_Ty* _Myptr; // the wrapped object pointer
};
2 auto_ptr的弊端
auto_ptr是用于C++11之前的智能指针。由于 auto_ptr 基于排他所有权模式:两个指针不能指向同一个资源,复制或赋值都会改变资源的所有权。auto_ptr 主要有如下几个问题:
- 复制和赋值会改变资源的所有权,不符合人的直觉。
- 在 STL 容器中使用auto_ptr存在重大风险,因为容器内的元素必需支持可复制(copy constructable)和可赋值(assignable)。
- 不支持对象数组的操作。
#include <stdio.h>
#include <iostream>
#include <string>
#include <memory>
#include <vector>
using namespace std;
int main() {
//弊端1. auto_ptr 被C++11 抛弃的主要理由 p1= p2 ,复制或赋值都会改变资源的所有权
auto_ptr<string> p1(new string("I 'm martin."));
auto_ptr<string> p2(new string("I 'm rock."));
printf("p1: %p\n", p1.get());
printf("p2: %p\n", p2.get());
p1 = p2;
printf("after p1 = p2\n");
printf("p1: %p\n", p1.get());
printf("p2: %p\n", p2.get());
//弊端2. 在 STL 容器中使用auto_ptr存在重大风险,因为容器内的元素必需支持可复制(copy constructable)和可赋值(assignable)。
vector<auto_ptr<string>> va;
auto_ptr<string> p3(new string("I 'm p3."));
auto_ptr<string> p4(new string("I 'm p4."));
va.push_back(std::move(p3));
va.push_back(std::move(p4));
cout <<"va[0]: "<< *va[0] << endl;
cout <<"va[1]: "<< *va[1] << endl;
//风险来啦
va[0] = va[1];
cout << "va[0]: " << *va[0] << endl;
cout << "va[1]: " << *va[1] << endl;
//弊端3. 不支持对象数组的内存管理
//auto_ptr<int[]> ai(new int[5]); //不能这样定义
//auto_ptr 陷阱,不能把同一段内存交给多个auto_ptr 变量去管理
/*{
auto_ptr<string> p2;
string* str = new string("智能指针的内存管理陷阱");
p2.reset(str);
{
auto_ptr<string> p1;
p1.reset(str);
}
cout <<"str: " << *p2 << endl;
}*/
system("pause");
return 0;
}
所以,C++11用更严谨的unique_ptr 取代了auto_ptr!
参考资料: