轉自:https://www.cnblogs.com/DswCnblog/p/5628195.html
成員函數
(1) get 獲得內部對象的指針, 由於已經重載了()方法, 因此和直接使用對象是一樣的.如 unique_ptr<int> sp(new int(1)); sp 與 sp.get()是等價的
(2) release 放棄內部對象的所有權,將內部指針置為空, 返回所內部對象的指針, 此指針需要手動釋放
(3) reset 銷毀內部對象並接受新的對象的所有權(如果使用缺省參數的話,也就是沒有任何對象的所有權, 此時僅將內部對象釋放, 並置為空)
(4) swap 交換兩個 shared_ptr 對象(即交換所擁有的對象) std::move(up) 所有權轉移(通過移動語義), up所有權轉移后,變成“空指針” (up 的定義為 std::unique_ptr<Ty> up)
unique_ptr 不支持拷貝和賦值.
std::unique_ptr<A> up1(new A(5));
std::unique_ptr<A> up2(up1); // 錯誤, unique_ptr 不支持拷貝
std::unique_ptr<A> up2 = up1; // 錯誤, unique_ptr 不支持賦值
雖然 unique_ptr 不支持拷貝和賦值, 但是我們可以調用 release 或 reset 將指針的所有權從一個(非 const) unique_ptr 轉移到另一個.
std::unique_ptr<int> up1(new int(1));
std::unique_ptr<int> up2(up1.release());
雖然 unique_ptr 不支持拷貝, 但是可以從函數中返回, 甚至返回局部對象. 如下面的代碼, 編譯器知道要返回的對象即將被銷毀, 因此執行一種特殊的"拷貝":
template <class Ty>
std::unique_ptr<Ty> Clone(const Ty& obj)
{ return std::unique_ptr<Ty>(new Ty(obj)); }
template <class Ty>
std::unique_ptr<Ty> Clone(const Ty& obj)
{ std::unique_ptr<Ty> temp = std::unique_ptr<Ty>(new Ty(obj)); return temp; }
unique_ptr 不共享它的指針。它無法復制到其他 unique_ptr,無法通過值傳遞到函數,也無法用於需要副本的任何標准模板庫 (STL) 算法。只能移動unique_ptr。這意味着,內存資源所有權將轉移到另一 unique_ptr,並且原始 unique_ptr 不再擁有此資源。我們建議你將對象限制為由一個所有者所有,因為多個所有權會使程序邏輯變得復雜。因此,當需要智能指針用於純 C++ 對象時,可使用 unique_ptr,而當構造 unique_ptr 時,可使用make_unique Helper 函數。
std::unique_ptr實現了獨享所有權的語義。一個非空的std::unique_ptr總是擁有它所指向的資源。轉移一個std::unique_ptr將會把所有權也從源指針轉移給目標指針(源指針被置空)。拷貝一個std::unique_ptr將不被允許,因為如果你拷貝一個std::unique_ptr,那么拷貝結束后,這兩個std::unique_ptr都會指向相同的資源,它們都認為自己擁有這塊資源(所以都會企圖釋放)。因此std::unique_ptr是一個僅能移動(move_only)的類型。當指針析構時,它所擁有的資源也被銷毀。默認情況下,資源的析構是伴隨着調用std::unique_ptr內部的原始指針的delete操作的。
下圖演示了兩個 unique_ptr 實例之間的所有權轉換。

1、如何創建unique_ptr
unique_ptr不像shared_ptr一樣擁有標准庫函數make_shared來創建一個shared_ptr實例。要想創建一個unique_ptr,我們需要將一個new 操作符返回的指針傳遞給unique_ptr的構造函數。
示例:
int main() { // 創建一個unique_ptr實例 unique_ptr<int> pInt(new int(5)); cout << *pInt; }
2、無法進行復制構造和賦值操作
unique_ptr沒有copy構造函數,不支持普通的拷貝和賦值操作。
int main() { // 創建一個unique_ptr實例 unique_ptr<int> pInt(new int(5)); unique_ptr<int> pInt2(pInt); // 報錯 unique_ptr<int> pInt3 = pInt; // 報錯 }
3、可以進行移動構造和移動賦值操作
unique_ptr雖然沒有支持普通的拷貝和賦值操作,但卻提供了一種移動機制來將指針的所有權從一個unique_ptr轉移給另一個unique_ptr。如果需要轉移所有權,可以使用std::move()函數。
示例:
int main() { unique_ptr<int> pInt(new int(5)); unique_ptr<int> pInt2 = std::move(pInt); // 轉移所有權 //cout << *pInt << endl; // 出錯,pInt為空 cout << *pInt2 << endl; unique_ptr<int> pInt3(std::move(pInt2)); }
4、可以返回unique_ptr
unique_ptr不支持拷貝操作,但卻有一個例外:可以從函數中返回一個unique_ptr。
示例:
unique_ptr<int> clone(int p) { unique_ptr<int> pInt(new int(p)); return pInt; // 返回unique_ptr } int main() { int p = 5; unique_ptr<int> ret = clone(p); cout << *ret << endl; }
使用舉例:
{
//創建一個指向int的空指針 std::unique_ptr<int> fPtr1; std::unique_ptr<int> fPtr2(new int(4)); auto fPtr3 = std::make_unique<int>(); //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_ptr<int> p(new int(5)); // ...(可能會拋出異常) }
2、返回函數內動態申請資源的所有權
unique_ptr<int> Func(int p) { unique_ptr<int> pInt(new int(p)); return pInt; // 返回unique_ptr } int main() { int p = 5; unique_ptr<int> ret = Func(p); cout << *ret << endl; // 函數結束后,自動釋放資源 }
3、在容器中保存指針
int main() { vector<unique_ptr<int>> vec; unique_ptr<int> p(new int(5)); vec.push_back(std::move(p)); // 使用移動語義 }
4、管理動態數組
標准庫提供了一個可以管理動態數組的unique_ptr版本。
int main() { unique_ptr<int[]> p(new int[5] {1, 2, 3, 4, 5}); p[0] = 0; // 重載了operator[] }
5、作為auto_ptr的替代品
創建與釋放舉例
#include <iostream>
#include <memory> #include <stdlib.h> 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<Foo> p1(new Foo); } //2. 普通的new[]對象. { std::unique_ptr<Foo[]> p2(new Foo[4]); } //3. 自定義的deleter. { std::unique_ptr<Foo, D> p3(new Foo); } } void TestOwner() { std::cout << "TestOwner...................." << std::endl; //1. new object. std::unique_ptr<Foo> p1(new Foo); // p1 owns Foo if (p1) p1->bar(); { std::unique_ptr<Foo> 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<Foo[]> p1(new Foo[4]); // p1 owns Foo if (p1) p1[0].bar(); { std::unique_ptr<Foo[]> 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
一下原文:https://www.cnblogs.com/wangkeqin/p/9383658.html
一個unique_ptr"擁有“他所指向的對象。與shared_ptr不同,某個時刻只能有一個unique_ptr指向一個給定的對象。當unique_ptr被銷毀時,它所指向的對象也被銷毀。uniptr_ptr表達的是一種獨占的思想。
初始化
#include <iostream>
#include <memory>
using namespace std;
//常規操作
int main(int argc, char *argv[])
{
unique_ptr<double> p1; //!可指向一個double的unique_ptr
unique_ptr<int> p2(new int(56)); //!p2指向了一個值為42的int
unique_ptr<string> pstr(new string("strtest"));
// unique_ptr<string> pstrCopy(pstr); //!error: 不支持對象的拷貝
unique_ptr<string> 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 <iostream>
#include <memory>
using namespace std;
class TEST
{
public:
TEST(const string & name)
:_name(name)
{cout<<"TEST:"<<_name<<endl;}
TEST(const TEST & another)
{ _name = another._name;
cout<<another._name<<" copyStruct "<<_name<<endl;}
TEST & operator =(const TEST & another){
if(&another==this)
return *this;
this->_name=another._name;
cout<<another._name<<" copyAssin to "<<_name<<endl;
}
~TEST(){cout<<"~TEST:"<<_name<<endl;}
//private:
string _name;
};
//其他操作
int main()
{
unique_ptr<TEST> p1(new TEST("case_1"));
unique_ptr<TEST> p2(p1.release()); //!將所有權從p1轉移到p2,p1現在指向NULL。
cout<<"++++++++++++++++++++++++"<<endl;
unique_ptr<TEST> p3(new TEST("case_2"));
p2.reset(p3.release()); //!p2釋放了原來指向的內存,接受了p3指向的內存。
getchar();
}
傳遞unique_ptr參數和返回unique_ptr
不能拷貝unique_ptr的規則有一個例外:我們可以拷貝或者賦值一個將要被銷毀的unique_ptr。其本質就是調用了移動拷貝和移動賦值;最常見的例子是從函數返回一個unique_ptr:
#include <iostream>
#include <memory>
using namespace std;
class TEST
{
public:
TEST(const string & name)
:_name(name)
{cout<<"TEST:"<<_name<<endl;}
TEST(const TEST & another)
{ _name = another._name;
cout<<another._name<<" copyStruct "<<_name<<endl;}
TEST & operator =(const TEST & another){
if(&another==this)
return *this;
this->_name=another._name;
cout<<another._name<<" copyAssin to "<<_name<<endl;
}
~TEST(){cout<<"~TEST:"<<_name<<endl;}
//private:
string _name;
};
//!例外:
//①返回一個即將被銷毀的uniptr
unique_ptr<TEST> retDying(string param)
{
return unique_ptr<TEST>(new TEST(param));
}
//②返回一個局部對象;
unique_ptr<TEST> retTemp(string param)
{
unique_ptr<TEST> pTemp(new TEST(param));
return pTemp;
}
int main()
{
unique_ptr<TEST>ret1 = retDying("dying");
cout<<(*ret1)._name<<endl;
unique_ptr<TEST>ret2 = retTemp("temp");
cout<<(*ret2)._name<<endl;
getchar();
}
向后兼容: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 <stdio.h>
#include <memory>
using namespace std;
void closePf(FILE * pf)
{
cout<<"----close pf after works!----"<<endl;
fclose(pf);
cout<<"*****end working****"<<endl;
}
int main()
{
// FILE * fp2 = fopen("bin2.txt", "w");
// if(!pf)
// return -1;
// char *buf = "abcdefg";
// fwrite(buf, 8, 1, fp2);
// fclose(fp2);
//______________________________________
// shared_ptr<FILE> pf(fopen("bin2.txt", "w"),closePf);
// cout<<"*****start working****"<<endl;
// if(!pf)
// return -1;
// char *buf = "abcdefg";
// fwrite(buf, 8, 1, pf.get()); //!確保fwrite不會刪除指針的情況下,可以將shared_ptr內置指針取出來。
// cout<<"----write int file!-----"<<endl;
unique_ptr<FILE,decltype(closePf)*> pf(fopen("bin2.txt", "w"),closePf); //!使用了decltype類型推斷
cout<<"*****start working****"<<endl;
if(!pf)
return -1;
char *buf = "abcdefg";
fwrite(buf, 8, 1, pf.get()); //!確保fwrite不會刪除指針的情況下,可以將unique_ptr內置指針取出來。
cout<<"----write int file!-----"<<endl;
return 0;
}
使用unique_ptr管理動態數組
標准庫提供了一個可以管理new分配動態數組的unique_ptr版本。為了用用一個unique_ptr管理動態數組,我們必須在對象類型后面跟一對空方括號;如此,在unique對象銷毀的時候,也可以自動調用delete[ ]而非delete來完成內存的釋放。
#include <iostream>
#include <memory>
using namespace std;
class ArrTest
{
public:
ArrTest(){
static int i = 0;
_i = i;
cout<<" ArrTest()"<<":"<<i++<<endl;
}
~ArrTest(){
static int i = 0;
cout<<"~ ArrTest()"<<":"<<i++<<endl;
}
int _i;
};
int main()
{
unique_ptr<ArrTest[]> p(new ArrTest[10]);
cout<<p[4]._i<<endl; //!獲取某個元素值,警告:不要使用越界的下標,unique_ptr也是不檢查越界的。
p.reset();
return 0;
}


