3.智能指针之unique_ptr
一、unique_ptrauto_ptr
void fun() { int *a = new int(1000); //do something delete a; }
return
c++unique_ptr
unique_ptr“exclusive ownership”(专属所有权)设计的,专属所有权就是确保了在同一时间一个指针拥有一个对象的全部资源。
unique_ptrp++
void fun() { unique_ptr<int> a(new int(1000)); //do something }
unique_ptr
unique_ptr<string>p1 = new string(“string”);//错误 unique_ptr<int>p2(new int(100));
unique_ptr
std::unique_ptr<std::string>p;//p
p = nullptr;p.reset();p
p
我们也可以这样去检测:
release()unique_ptr
std::string *p2 = p.release();//p == nullptr
unique_ptrunique_ptrexclusive ownership
std::string* sp = new std::string("hello"); std::unique_ptr<std::string> up1(sp); std::unique_ptr<std::string> up2(sp);//程序异常。 up1 and up2 own same data
copymove
std::unique_ptr<string> up1(new string); std::unique_ptr<string> up2(up1); //错误,无法通过编译 std::unique_ptr<string> up3(std::move(up1));//ok,move语义
std::unique_ptr<string> up1(new string); std::unique_ptr<string> up2; // create another unique_ptr up2 = up1; // ERROR: not possible up2 = std::move(up1);
看下面这几行代码:
std::unique_ptr<ClassA> up1(new ClassA); std::unique_ptr<ClassA> up2(new ClassA); up2 = std::move(up1);
up2moveup2up2delete
unique_ptrunique_ptr
ptr = new ClassA; // ERROR ptr = std::unique_ptr<ClassA>(new ClassA); // OK, delete old object
ptrnullptr
ptr = nullptr;//ptr.reset();
move
unique_ptr
void fun(unique_ptr<string>str) { cout << *str; } ... unique_ptr<string>str(new string("hello world")); fun(unique_ptr<string>(new string ("test"))); fun(std::move(str)); ...
:fun(unique_ptr<string>str)
unique_ptr
unique_ptr<string, void(*)(string*)>fun() { unique_ptr < string, void(*)(string*) > str(new string("1111"), [](string *str) { cout << *str; delete str; }); return str; } ... auto it = fun(); ...
move(),C++11move
二、unique_ptr
class ClassB { private: ClassA* ptr1; // pointer members ClassA* ptr2; public: // constructor that initializes the pointers // - will cause resource leak if second new throws ClassB(int val1, int val2) : ptr1(new ClassA(val1)), ptr2(new ClassA(val2)) { } // copy constructor // - might cause resource leak if second new throws ClassB(const ClassB& x) : ptr1(new ClassA(*x.ptr1)), ptr2(new ClassA(*x.ptr2)) { } // assignment operator const ClassB& operator= (const ClassB& x) { *ptr1 = *x.ptr1; *ptr2 = *x.ptr2; return *this; } ~ClassB() { delete ptr1; delete ptr2; } ... };
ptr2时抛出一个异常,那么将会造成ptr1的内存泄漏,为了避免该问题的发生,我们可以将代码中的普通指针更改为unique_ptr。
class ClassB { private: std::unique_ptr<ClassA> ptr1; // unique_ptr members std::unique_ptr<ClassA> ptr2; public: // constructor that initializes the unique_ptrs // - no resource leak possible ClassB (int val1, int val2) : ptr1(new ClassA(val1)), ptr2(new ClassA(val2)) { } // copy constructor // - no resource leak possible ClassB (const ClassB& x) : ptr1(new ClassA(*x.ptr1)), ptr2(new ClassA(*x.ptr2)) { } // assignment operator const ClassB& operator= (const ClassB& x) { *ptr1 = *x.ptr1; *ptr2 = *x.ptr2; return *this; } // no destructor necessary // (default destructor lets ptr1 and ptr2 delete their objects) ... };
ClassA也需要实现copy 构造函数以及赋值操作,否则将只会调用move构造函数
三、操作数组std::unique_ptr<std::string> up(new std::string[10]); // runtime ERROR
unique_ptr与shared_ptr有所区别,我们可以直接声明为:
std::unique_ptr<std::string[]> up1(new std::string[10]);//ok std::cout << *up << std::endl; // ERROR: * not defined for arrays std::cout << up[0] << std::endl; // OK,此时我们应该确保下标不能越界
unique_ptr内部中关于使用数组声明以及普通参数的区别。
namespace std { // primary template: template <typename T, typename D = default_delete<T>> class unique_ptr { public: ... T& operator*() const; T* operator->() const noexcept; ... }; // partial specialization for arrays: template<typename T, typename D> class unique_ptr<T[], D> { public: ... T& operator[](size_t i) const; ... } }
default_delete
namespace std { // primary template: template <typename T> class default_delete { public: void operator()(T* p) const; // calls delete p ... }; // partial specialization for arrays: template <typename T> class default_delete<T[]> { public: void operator()(T* p) const; // calls delete[] p ... }; }
std::unique_ptr<std::string> up1(new std::string[10]);//error std::unique_ptr<std::string[]> up1(new std::string[10]);//ok
四、
default_delete 与 default_delete[]无法满足我们的要求,此时我们需要定义我们自己的删除器,而我们在这里定义的删除器与在shared_ptr中的定义方式又有一些不同,在unique_ptr中我们必须将删除器的类型,作为模板的第二个参数声明,这个参数可以是函数引用,函数指针或者一个函数对象。
class ClassADeleter { public: void operator () (ClassA* p) { std::cout << "call delete for ClassA object" << std::endl; delete p; } };... std::unique_ptr<ClassA,ClassADeleter> up(new ClassA());
lambda表达式,但是也需要声明这个函数的类型,例如:
std::unique_ptr<int,void(*)(int*)> up(new int[10], [](int* p) { ... delete[] p; });
或者
auto dl = [](int* p) { ... delete[] p; }; std::unique_ptr<int,decltype(dl)>> up(new int[10], dl);
template <typename T> using uniquePtr = std::unique_ptr<T, void(*)(T*)>; // alias template ... uniquePtr<int> up(new int[10], [](int* p) { // used here ... delete[] p; });
注意:
unique_ptr<int>p1(new int); //OK unique_ptr<int>p2(new int[10]); //error可以编译但是程序会崩溃 unique_ptr<int[]>p3(new int[10]); /OK shared_ptr<int>p4(new int); //OK shared_ptr<int>p5(new int[10]); //可以通过编译但会崩溃 shared_ptr<int[]>p6(new int[10]); //无法通过编译
unique_ptr中允许将类型声明为void的,所以void*也是可以的
T被声明为T[]时,[]操作将会代替*和->
delete[]和delete两种