天天看點

C++ Primer Plus 書之--C++ 智能指針 auto_ptr, shared_ptr, unique_ptr智能指針:

智能指針:

智能指針的好處就是, 當智能指針過期時, 其析構函數将使用delete來釋放記憶體. 是以, 如果将new傳回的位址指派給這些對象, 将無需記住稍後釋放這些記憶體: 在隻能指針過期時, 這些記憶體将自動被釋放.

有三個隻能指針模闆: auto_ptr, unique_ptr 和 shared_ptr 都定義了類似指針的對象, 可以将new獲得(直接或間接)的位址賦給這種對象.

要建立隻能指針對象, 必須包含頭檔案memory, 然後使用通常的模闆文法來執行個體化所需類型的指針, 例如:

template<class X> class auto_ptr {
public:
	explicit auto_ptr(X * p = 0) throw();
	...
}
// pd是一個指向double的auto_ptr
auto_ptr<double> pd(new double);
// ps是一個執行string的auto_ptr, 相當于string * ps;
auto_ptr<string> ps(new string);
           

其他兩種智能指針的用法相同:

unique_ptr<double> pdu(new double);
shared_ptr<string> pss(new string);
           

智能指針模闆位于名稱空間std中. 下面我們來看一個完整的例子:

// smartptrs.cpp
#include <iostream>
#include <string>
#include <memory>

class Report
{
private:
	std::string str;
public:
	Report(const std::string s): str(s) {
		std::cout << "Object created!" << std::endl;
	}
	~Report() {
		std::cout << "Object deleted!\n"; 
	}
	void comment() const {std::cout << str << "\n"; }
};

int main()
{
	{
		std::auto_ptr<Report> ps(new Report("using auto_ptr"));
		ps->comment();
	}
	{
		std::shared_ptr<Report> ps(new Report("using shared_ptr"));
		ps->comment();
	}
	{
		std::unique_ptr<Report> ps(new Report("using unique_ptr"));
		ps->comment();
	}
	return 0;
}
           

程式運作結果為:

C++ Primer Plus 書之--C++ 智能指針 auto_ptr, shared_ptr, unique_ptr智能指針:

所有智能指針類都有一個explicit構造函數, 該構造函數将指針作為參數. 是以不需要自動将指針轉換為智能指針對象:

shared_ptr<double> pd;
double * p_reg = new double;
// 這個是非法的轉換, 因為p_reg是指針, 而pd是隻能指針對象
pd = p_reg;
// 合法的
pd = shared_ptr<double>(p_reg);
// 非法的
shared_ptr<double> pshared = p_reg;
// 合法的
shared_ptr<double> pshared(p_rege=);
           

需要注意的一點: 對全部三種智能指針都應避免的一點:

string vacation("I wandered lonely as a cloud");
shared_ptr<string> pvac(&vacation);
           

pvac過期時, 程式将把delete運算符用于非堆記憶體, 這是錯誤的.

有關智能指針的注意事項:

先看下面的指派語句:

auto_ptr<string> ps(new string("I reigned lonely as a cloud"));
auto_ptr<string> vocation;
vocation = ps;
           

如果ps和vocation是正常指針, 則兩個指針将指向同一個string對象, 這是不能接受的, 因為程式将試圖删除同一個對象兩次--一次是ps過期時, 另一次是vocation過期時, 為避免這種問題, 有以下幾種方法:

1. 定義指派運算符, 使之執行深複制. 這樣兩個指針将指向不同的對象, 其中一個對象是另一個對象的副本.

2. 建立所有權(ownership)概念, 對于特定的對象, 隻能有一個隻能指針可擁有它, 這樣隻有擁有對象的隻能指針的構造函數會删除該對象, 然後, 讓指派操作轉讓所有權. 這就是auto_ptr和unique_ptr的政策, 但unique_ptr的政策更嚴格.

3. 建立智能更高的指針, 跟蹤引用特定對象的智能指針數, 稱之為引用計數. 例如: 指派時, 計數将加1, 而指針過期時, 計數将減1. 僅當最後一個指針過期時, 才調用delete. 這是 shared_ptr 采用的政策.

來看一個完整的例子:

// fowl.cpp, 智能指針的例子
#include <iostream>
#include <string>
#include <memory>

int main()
{
	using namespace std;
	auto_ptr<string> films[5] = {
		auto_ptr<string> (new string("First word")),
		auto_ptr<string> (new string("Second word")),
		auto_ptr<string> (new string("Third word")),
		auto_ptr<string> (new string("Fourth word")),
		auto_ptr<string> (new string("Fifth word"))
	};
	auto_ptr<string> pwin;
	pwin = films[2];
	cout << "The nominees for best avian baseball film are \n";
	for(int i = 0; i < 5; i++)
		cout << *films[i] << endl;
	cout << "The winner is " << *pwin << endl;
	cin.get();
	return 0;
}
           

程式運作結果為:

C++ Primer Plus 書之--C++ 智能指針 auto_ptr, shared_ptr, unique_ptr智能指針:

請看下面的語句:

auto_ptr<string> p1(new string("auto"));
auto_ptr<string> p2;
p2 = p1;
           

在第三行代碼中, p2接管string對象的所有權後, p1的所有權将被剝奪. 但如果程式随後試圖使用p1, 這件更引起異常, 因為p1不再指向有效的資料. 這也正是上面的fowl.cpp運作出錯的原因.

如果我們換成unique_ptr, 會有不同的效果:

unique_ptr<string> p3(new string("auto"));
unique_ptr<string> p4;
p4 = p3;
           

編譯器會認為上面第三行非法, 這樣就避免了p3不再指向有效資料的問題. 是以unique_ptr比 auto_ptr更安全.

 但是注意一點: 程式試圖将一個unique_ptr賦給另一個時, 如果原unique_ptr是一個臨時右值, 則編譯器允許這樣使用, 但如果原unique_ptr将存在一段時間, 編譯器将禁止這樣做. 例如:

using namespace std;
unique_ptr<string> pu1(new string("Hello"));
unique_ptr<string> pu2;
// 非法的
pu2 = pu1;
unique_ptr<string> pu3;
// 合法的.
pu3 = unique_ptr<string>(new string("world"));
           

如果我們一定要使用上述的pu2 = pu1; 這種操作. 僅當以非智能的方式使用遺棄的智能指針(如接觸引用時), 這種指派才不安全. 要安全地重用這種指針, 可以給它賦新值. C++提供了一個标準庫函數std::move(), 讓我們能夠講一個unique_ptr賦給另一個. 來看一個例子:

using namespace std;
unique_ptr<string> demo(const char * s)
{
	unique_ptr<string> temp (new string(s));
	return temp;
}

unique_ptr<string> ps1, ps2;
ps1 = demo("Uniquely special");
ps2 = move(ps1);
ps1 = demo(" and more");
cout << *ps2 << *ps1 << endl;
           

相比于auto_ptr, unique_ptr還有另一個優點. 它有一個用于數組的變體. 注意, 必須将delete和new配對使用, 将delete[]和new[]配對使用. 模版auto_ptr使用delete而不是delete[], 是以隻能與new一起使用, 而不能與new[]一起使用. 但unique_ptr有使用new[]和delete[]的版本:

std::unique_ptr<double[]> pda(new double[5]);
           

如何選擇智能指針:

1. 如果程式要使用多個指向同一個對象的指針, 應選擇shared_ptr.

2. 如果程式不需要多個指向同一個對象的指針, 則可以使用unique_ptr. 如果函數使用new配置設定記憶體, 并傳回指向該記憶體的指針, 推薦使用unique_ptr.

3. 在unique_ptr為右值時, 可将其賦給shared_ptr.

繼續閱讀