标签:
本节研究智能指针的相关问题;
template <typename T> class SmartPointer { public: SmartPointer(const SmartPointer&) = delete; SmartPointer& operator=(const SmartPointer&) = delete; explicit SmartPointer(T* p= NULL) :_pointer(p) { } ~SmartPointer() { delete _pointer; } T* operator->() const { return _pointer; } T& operator*() const { return *_pointer; } T* get() const { return _pointer; } T* release() { T* p = _pointer; _pointer = NULL; return p; } void reset(T* p = NULL) { if (p != _pointer) { delete _pointer; _pointer = p; } } private: T* _pointer; };说明几点:
(1)SmartPointer类似unique_ptr一样,不支持拷贝构造和赋值,是对auto_ptr的加强版;
#include <vector> #include <iostream> using namespace std; int main(void) { vector<int*> v; { int x = 10; v.push_back(&x); v.push_back(new int(2)); } cout << v[0] << " "<< v[1] << endl; cout << *v[0] << " "<< *v[1] << endl; }
说明几点:
(1)当v离开块作用域(两次push_back所在的作用域时),对v[0]的访问将会是非法指针访问;栈对象已经析构,v[0]所访问的内存的内容已经不存在;虽然本题输出仍正确,因为x变量所在的内存未被重新利用;
(2)当v离开main作用域时,v发生了内存泄露,需要显式delete,因为原始指针(包括类的原始指针)和int,long long这样的基本数据类型一样,不会执行析构函数,内存泄漏;而要自己显示执行delete v[0];
代码片段如下:
#include <iostream> #include <vector> #include <utility> using namespace std; class A { public: A() = default; A(const A& a) { cout << "copy ctor" << endl; } A(A&& a) noexcept { cout << "move ctor" << endl; } }; int main(void) { vector<A> rawVec; rawVec.reserve(10); cout << "------------------\n"; rawVec.push_back(A()); cout << "------------------\n"; rawVec.push_back(*(new A())); cout << "------------------\n"; rawVec.push_back(std::move(*(new A()))); }
说明几点:
(1)rawVec.push_back(A());首先产生一个临时对象,由于A提供了移动构造函数,容器将直接调用移动构造函数;然后临时对象自动销毁;
(2)push_back(*(new A());首先new int()创建了一个堆内存,然后解引用将其拷贝构造到vector中,vector本身的管理数据在栈内存中,而真正的数据也在堆内存中,由stl的空间配置器分配);push_back(*(new A())将会内存泄漏,因为push_back()中放入的对象是拷贝的新对象,而最后离开main函数后,vector销毁该对象时,析构的也是该新对象,原有堆内存对象不会析构,内存泄漏;
(3)push_back(std::move(*(new A())));首先new int()创建了一个堆内存,然后解引用并使用std::move将其移动构造到vector中,然而new A()本身所占用的内存(管理)将会泄漏;
(4)当rawVec离开main作用块时,会对其每一个对象调用析构函数;
(5)因为A()移动构造并未做任何事情,下面以A为string作为替换,假设string实现移动构造函数,而字符串值作为移动构造函数进行提高效率的关键部分;
(5.1)对于rawVec.push_back(string("123")); rawVec将会使用移动构造接管“123”的内存,而本身string临时对象的管理数据将会自动销毁;
(5.2)对于rawVec.push_back(*(new string("123")));rawVec将会使用拷贝构造复制“123”的内存,而new string("123")的管理数据以及"123"都将会泄露;
(5.3)对于rawVec.push_back(std::move(*(new string("123"))));rawVec将会使用移动构造接管“123”的内存,而new string("123")的管理数据会泄露,“123”本身不会泄露,而被vector中的对象接管;
std::shared_ptr<int> p1 = std::make_shared<int>(); std::shared_ptr<int> p2(new int);说明几点:
(1)在shared_ptr中,管理信息至少包括一个引用计数count和一个指向对象的指针;对于p1的产生,new int和管理信息的内存是一次性申请出来的;
(2)而p2的产生,首先会先new一个int,然后再申请管理信息的内存,总共会有两次内存申请;当涉及到多个std::shared_ptr<int> p(new int)对象分配时,可能会导致内存泄露;
shared_ptr二次释放问题
#include <iostream> #include <memory> using namespace std; class A { public: A() = default; ~A() { cout << "dtor" << endl; } }; void fun(shared_ptr<A> a) { } int main(void) { A *x(new A); fun(shared_ptr<A>(x)); cout << "-------------\n"; shared_ptr<A> p(x); }
说明几点:
(1)fun()中shared_ptr已经接管了x所指向的堆内存,并将释放;而 当shared_ptr<A> p(x);接管的x指向内存已经释放,造成二次释放,造成段错误;
(2)使用shared_ptr的get()的原始指针,也有可能出现二次释放的问题;因为对get()原始指针进行shared_ptr将会单独创建,而不是共享;
#include <iostream> #include <memory> using namespace std; class A { public: A() = default; ~A() { cout << "dtor" << endl; } }; int main(void) { A *x(new A[10]); shared_ptr<A> a(x , [](A *p) { delete []p; } ); }说明几点:
(1)因为shared_ptr不直接支持动态数组,默认的删除器是delete p;因此需要显式传入删除器;
(2)栈对象有时也可以需要shared_ptr来管理,显然我们不能使用默认的删除器,也需要传入删除器;
#include <iostream> #include <memory> using namespace std; class A { public: A() = default; ~A() { cout << "dtor" << endl; } }; int main(void) { unique_ptr<A> a(new A); a.release(); unique_ptr<A[]> b(new A[1]); b.reset(); }说明几点:
(1)a.realase()将会转换对指针的控制权,但是如果没有新的unique_ptr接管,那么将会存在内存泄露,应该使用reset进行显式的内存释放;
(2)a所管理的内存,release()将会内存泄露;而b.reset将会显式释放控制,并销毁内存;
(3)unique_ptr不同于shared_ptr,直接支持动态数组;
标签:
原文地址:http://blog.csdn.net/skyuppour/article/details/45626507