成员函数
(1) get 获得内部对象的指针, 由于已经重载了()方法, 因此和直接使用对象是一样的.如 unique_ptr
(2) release 放弃内部对象的所有权,将内部指针置为空, 返回所内部对象的指针, 此指针需要手动释放
(3) reset 销毁内部对象并接受新的对象的所有权(如果使用缺省参数的话,也就是没有任何对象的所有权, 此时仅将内部对象释放, 并置为空)
(4) swap 交换两个 shared_ptr 对象(即交换所拥有的对象) std::move(up) 所有权转移(通过移动语义), up所有权转移后,变成“空指针” (up 的定义为 std::unique_ptr
unique_ptr 不支持拷贝和赋值.
std::unique_ptr
构造函数。
示例:
int main() { // 创建一个unique_ptr实例 unique_ptrpint(new int(5)); cout << *pint; }
2、无法进行复制构造和赋值操作
unique_ptr没有copy构造函数,不支持普通的拷贝和赋值操作。
int main() { // 创建一个unique_ptr实例 unique_ptrpint(new int(5)); unique_ptr pint2(pint); // 报错 unique_ptr pint3 = pint; // 报错 }
3、可以进行移动构造和移动赋值操作
unique_ptr虽然没有支持普通的拷贝和赋值操作,但却提供了一种移动机制来将指针的所有权从一个unique_ptr转移给另一个unique_ptr。如果需要转移所有权,可以使用std::move()函数。
示例:
int main() { unique_ptrpint(new int(5)); unique_ptr pint2 = std::move(pint); // 转移所有权 //cout << *pint << endl; // 出错,pint为空 cout << *pint2 << endl; unique_ptr pint3(std::move(pint2)); }
4、可以返回unique_ptr
unique_ptr不支持拷贝操作,但却有一个例外:可以从函数中返回一个unique_ptr。
示例:
unique_ptrclone(int p) { unique_ptr pint(new int(p)); return pint; // 返回unique_ptr } int main() { int p = 5; unique_ptr ret = clone(p); cout << *ret << endl; }
使用举例: { //创建一个指向int的空指针 std::unique_ptrfptr1; std::unique_ptr fptr2(new int(4)); auto fptr3 = std::make_unique (); //fptr2释放指向对象的所有权,并且被置为nullptr std::cout << "fptr2 release before:" << fptr2.get() << std::endl; int *pf = fptr2.release(); std::cout << "fptr2 release before:" << fptr2.get() << " and pf value:" << *pf << std::endl; //所有权转移,转移后fptr3变为空指针 std::cout << "move before fptr1 address:" << fptr1.get() << " fptr3 address:" << fptr3.get() << std::endl; fptr1 = std::move(fptr3); std::cout << "move after fptr1 address:" << fptr1.get() << " fptr3 address:" << fptr3.get() << std::endl; std::cout << "move before fptr1 address:" << fptr1.get() << std::endl; fptr1.reset(); std::cout << "move after fptr1 address:" << fptr1.get() << std::endl; } 输出: fptr2 release before:00efb120 fptr2 release before:00000000 and pf value:4 move before fptr1 address:00000000 fptr3 address:00efec60 move after fptr1 address:00efec60 fptr3 address:00000000 move before fptr1 address:00efec60 move after fptr1 address:00000000
unique_ptr使用场景
1、为动态申请的资源提供异常安全保证
我们先来看看下面这一段代码:
void func() { int *p = new int(5); // ...(可能会抛出异常) delete p; }
这是我们传统的写法:当我们动态申请内存后,有可能我们接下来的代码由于抛出异常或者提前退出(if语句)而没有执行delete操作。
解决的方法是使用unique_ptr来管理动态内存,只要unique_ptr指针创建成功,其析构函数都会被调用。确保动态资源被释放。
void func() { unique_ptrp(new int(5)); // ...(可能会抛出异常) }
2、返回函数内动态申请资源的所有权
unique_ptrfunc(int p) { unique_ptr pint(new int(p)); return pint; // 返回unique_ptr } int main() { int p = 5; unique_ptr ret = func(p); cout << *ret << endl; // 函数结束后,自动释放资源 }
3、在容器中保存指针
int main() { vector> vec; unique_ptr p(new int(5)); vec.push_back(std::move(p)); // 使用移动语义 }
4、管理动态数组
标准库提供了一个可以管理动态数组的unique_ptr版本。
int main() { unique_ptrp(new int[5] {1, 2, 3, 4, 5}); p[0] = 0; // 重载了operator[] }
5、作为auto_ptr的替代品
创建与释放举例
#include#include #include struct foo { foo() { std::cout << "foo::foo\n"; } ~foo() { std::cout << "foo::~foo\n"; } void bar() { std::cout << "foo::bar\n"; } }; void f(const foo &) { std::cout << "f(const foo&)\n"; } struct d { void operator()(foo* foo) { std::cout << "d operator()" << std::endl; delete foo; } }; void testautodestroy() { //1. 普通的new对象. std::cout << "testdestroy...................." << std::endl; { std::unique_ptr p1(new foo); } //2. 普通的new[]对象. { std::unique_ptr p2(new foo[4]); } //3. 自定义的deleter. { std::unique_ptr p3(new foo); } } void testowner() { std::cout << "testowner...................." << std::endl; //1. new object. std::unique_ptr p1(new foo); // p1 owns foo if (p1) p1->bar(); { std::unique_ptr p2(std::move(p1)); // now p2 owns foo f(*p2); p1 = std::move(p2); // ownership returns to p1 p2->bar(); std::cout << "destroying p2...\n"; } p1->bar(); } void testarrayowner() { std::cout << "testarrayowner...................." << std::endl; //1. new[] object. std::unique_ptr p1(new foo[4]); // p1 owns foo if (p1) p1[0].bar(); { std::unique_ptr p2(std::move(p1)); // now p2 owns foo f(p2[0]); p1 = std::move(p2); // ownership returns to p1 p2[0].bar(); std::cout << "destroying p2...\n"; } p1[0].bar(); } int main() { testautodestroy(); testowner(); testarrayowner(); }
输出:
testdestroy.................... foo::foo foo::~foo foo::foo foo::foo foo::foo foo::foo foo::~foo foo::~foo foo::~foo foo::~foo foo::foo d operator() foo::~foo testowner.................... foo::foo foo::bar f(const foo&) foo::bar destroying p2... foo::bar foo::~foo testarrayowner.................... foo::foo foo::foo foo::foo foo::foo foo::bar f(const foo&) foo::bar destroying p2... foo::bar foo::~foo foo::~foo foo::~foo foo::~foo
一个unique_ptr"拥有“他所指向的对象。与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定的对象。当unique_ptr被销毁时,它所指向的对象也被销毁。uniptr_ptr表达的是一种独占的思想。
初始化
#include#include using namespace std; //常规操作 int main(int argc, char *argv[]) { unique_ptr p1; //!可指向一个double的unique_ptr unique_ptr p2(new int(56)); //!p2指向了一个值为42的int unique_ptr pstr(new string("strtest")); // unique_ptr pstrcopy(pstr); //!error: 不支持对象的拷贝 unique_ptr pstrassin; // pstrassin = pstr //!error: uniptr不支持赋值 return 0; }
unique_ptr一般操作
关于unique_ptr还支持哪些操作,在前面的博文中我也做了总结,请参考该篇文章中图表:https://www.cnblogs.com/wangkeqin/p/9351191.html
unique_ptr所有权转移
虽然我们不能拷贝赋值unique_ptr,但是可以通过调用release或者set将指针的所有权从一个(非const)unique_ptr转移给一个unique:
#include#include using namespace std; class test { public: test(const string & name) :_name(name) {cout<<"test:"<<_name< _name=another._name; cout< p1(new test("case_1")); unique_ptr p2(p1.release()); //!将所有权从p1转移到p2,p1现在指向null。 cout<<" "< p3(new test("case_2")); p2.reset(p3.release()); //!p2释放了原来指向的内存,接受了p3指向的内存。 getchar(); }
传递unique_ptr参数和返回unique_ptr
不能拷贝unique_ptr的规则有一个例外:我们可以拷贝或者赋值一个将要被销毁的unique_ptr。其本质就是调用了移动拷贝和移动赋值;最常见的例子是从函数返回一个unique_ptr:
#include#include using namespace std; class test { public: test(const string & name) :_name(name) {cout<<"test:"<<_name< _name=another._name; cout< retdying(string param) { return unique_ptr (new test(param)); } //②返回一个局部对象; unique_ptr rettemp(string param) { unique_ptr ptemp(new test(param)); return ptemp; } int main() { unique_ptr ret1 = retdying("dying"); cout<<(*ret1)._name< ret2 = rettemp("temp"); cout<<(*ret2)._name<
向后兼容:auto_ptr
标准库较早的版本包含了一个名为auto_ptr的类,它具有unique_ptr的部分特性,但不是全部。特别时我们在容器中保存auto_ptr,也不能从函数中返回auto_ptr。虽然auto_ptr仍然是标准库的一部分,但是编写程序时应该使用unique_ptr。
向unique_ptr传递删除器
类似于shared_ptr,unique_ptr默认情况下也是使用delete释放它指向的对象。与shared_ptr一样,我们可以重载一个unique_ptr中默认的删除器。但是unique_ptr管理删除器的方式与shared_ptr不同,其原因我们将在后面继续补充。
重载一个unique_ptr中的删除器会影响到unique_ptr类型如何构造(或reset)该类型的对象。与重载关联器的比较操作类似。我们必须在尖括号中unique_ptr指向类型之后提供删除器类型。在创建或者reset一个这种unique_ptr这种类型的对象时,必须提供一个指定类型的可调用对象:
#include#include using namespace std; void closepf(file * pf) { cout<<"----close pf after works!----"< pf(fopen("bin2.txt", "w"),closepf); // cout<<"*****start working****"< pf(fopen("bin2.txt", "w"),closepf); //!使用了decltype类型推断 cout<<"*****start working****"<
使用unique_ptr管理动态数组
标准库提供了一个可以管理new分配动态数组的unique_ptr版本。为了用用一个unique_ptr管理动态数组,我们必须在对象类型后面跟一对空方括号;如此,在unique对象销毁的时候,也可以自动调用delete[ ]而非delete来完成内存的释放。
#include#include using namespace std; class arrtest { public: arrtest(){ static int i = 0; _i = i; cout<<" arrtest()"<<":"< p(new arrtest[10]); cout<