C++ 補充 & C++ 11 - C++智能指針unique_ptr 使用詳解 (C++11)

unique_ptr 使用詳解 (C++11)

auto_ptr是用於C++11之前的智能指針。由於 auto_ptr 基於排他所有權模式:兩個指針不能指向同一個資源,複製或賦值都會改變資源的所有權。auto_ptr 主要有兩大問題:

複製和賦值會改變資源的所有權,不符合人的直覺。

在 STL 容器中使用auto_ptr存在重大風險,因爲容器內的元素必需支持可複製(copy constructable)和可賦值(assignable)。

不支持對象數組的操作

demo 代碼(一)

例子:

#include <iostream>
#include <stdio.h>
#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 weifc."));
	auto_ptr<string> p2(new string("I 'm rock."));

	printf("p1: %p\n", p1, 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;
		p2.reset(str);
		{
			auto_ptr<string> p1;
			p1.reset(str);
		}
		cout << "str: " << p2 << endl;
	}*/

	system("pause");
	return 0;
}

所以, C++11 用更嚴謹的 unique_ptr 取代了 auto_ptr !

unique_ptr 特性

  1. 基於排他所有權模式: 兩個指針不能指向同一資源
  2. 無法進行左值 unique_ptr 複製構造, 也無法進行左值複製賦值操作, 但允許臨時右值賦值構造和賦值
  3. 保存指定某個對象的指針, 當它本身離開作用域會自動釋放它指向的對象.
  4. 在容器中保存指針是安全的

demo 代碼(二)

#include <stdio.h>
#include <iostream>
#include <string>
#include <memory>
#include <vector>

using namespace std;

int main()
{
	/* 弊端 1. auto_ptr 被C++11 拋棄的主要理由 p1 = p2, 複製或賦值都會改變資源的所有權 */
	/* unique_ptr 如何解決這個問題? 不允許顯示的右值賦值和構造 */
	unique_ptr<string> p1(new string("I 'm weifc"));
	unique_ptr<string> p2(new string("I 'm weikc"));
	printf("p1: %p\n", p1.get());
	printf("p2: %p\n", p2.get());

	/* 如果一定要轉移, 使用 move 把左值轉成右值 */
	p1 = std::move(p2);
	printf("p1: %p\n", p1.get());
	printf("p2: %p\n", p2.get());

	/* p1 = p2; 左值賦值禁止 */
	unique_ptr<string> p3(new string("I 'm p3."));
	unique_ptr<string> p4(std::move(p3)); /* 左值拷貝構造也不行, 必須轉成右值 */

	/* 弊端2. 在 STL 容器中使用 auto_ptr 存在重大風險, 因爲容器內的元素必需支持可複製(copy constructable) 
	和 可賦值 (assignable). */
	vector<unique_ptr<string>> vu;
	unique_ptr<string> p5(new string("I 'm p5"));
	unique_ptr<string> p6(new string("I 'm p6"));

	vu.push_back(std::move(p3));
	vu.push_back(std::move(p4));

	cout << "va[0]: " << *vu[0] << endl;
	cout << "va[1]: " << *vu[1] << endl;

	// vu[0] = vu[1]; // unique_ptr 不支持直接賦值, 沒有風險

	/* 弊端 3. auto_ptr 不支持對象數組的內存管理, unique_ptr 支持 */
	// 但是 unique_ptr 支持對象數組的管理
	// auto_ptr<int[]> ai(new int[5]); // 不能這樣定義
	unique_ptr<int[]> ui(new int[5]); // 自動會調用 delete [] 函數去釋放
	
	system("pause");
	return 0;
}

構造函數

unique_ptr<T> up ; //空的unique_ptr,可以指向類型爲T的對象
unique_ptr<T> up1(new T()) ;//定義unique_ptr,同時指向類型爲T的對象
unique_ptr<T[]> up ; //空的unique_ptr,可以指向類型爲T[的數組對象
unique_ptr<T[]> up1(new T[]) ;//定義unique_ptr,同時指向類型爲T的數組對象
unique_ptr<T,D> up(); //空的unique_ptr,接受一個D類型的刪除器d,使用d釋放內存
unique_ptr<T,D> up(new T()); //定義unique_ptr,同時指向類型爲T的對象,接受一個D							  類型的刪除器d,使用刪除器d來釋放內存

賦值

unique_ptr<int> up1(new int(10));
unique_ptr<int> up2(new int(11));
up1 = std::move(up2);//必須使用移動語義,結果,up1 內存釋放, up2 交由up1 管理

主動釋放對象

up = nullptr ;//釋放up指向的對象,將up置爲空
或  up = NULL; //作用相同 

放棄對象控制權

up.release();    //放棄對象的控制權,返回指針,將up置爲空,不會釋放內存

重置

up.reset()//參數可以爲 空、內置指針,先將up所指對象釋放,然後重置up的值

交換

up.swap(up1);  //將智能指針up 和up1管控的對象進行交換

結語:

時間: 2020-07-03

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章