1. 程式人生 > >C++——智慧指標

C++——智慧指標

你可能經常性的幹如下的事情。

void remodel(std::string & str)
{
    std::string *ps = new std::string(str);
    
    str = ps;
    return;
}

其實你也不願意會發生上述事情,一般來說,程式設計師寫的是介面,外部呼叫該函式的時候,久而久之會發現記憶體水漲船高,歸根結底就是記憶體洩露導致的。

雖然你可以每次很注意要delete,但是保不齊你不需要立即釋放,或者也會遺漏忘記,甚至你不知道什麼時候釋放。然道沒有一種方法能幫你跳出這種“處境”嗎?

本文所有說得就是運用c++標準庫提供的智慧指標。簡單來說,

智慧指標(智慧指標模板類)是行為類似於指標的類物件。用於幫助管理動態記憶體分配的智慧指標模板。

雖然有解構函式的存在,也只能解決類物件的釋放。始終不能解決指標的釋放。

C++98提供了auto_ptr(C++11已將其摒棄),C++11提供了另外兩種解決方案,unique_ptr, shared_ptr。

使用方法如下:

#include <iostream>
#include <memory>
#include <string>
using namespace std;


class Report
{
private:
	string str;
public:
	Report(const string s) :str(s) { cout << "Object Created!" << endl; }
	~Report() { cout << "Object Deleted" << endl; }
	void comment() const { cout << str << endl; }
};


void main()
{
	{
		auto_ptr<Report> ps(new Report("using auto_ptr"));
		ps->comment();
	}
	{
		shared_ptr<Report> ps(new Report("using shared_ptr"));
		ps->comment();
	}
	{
		unique_ptr<Report> ps(new Report("using unique_ptr"));
		ps->comment();
	}
	system("pause");
	return;

}

所有智慧指標都有explicit建構函式,只能顯示呼叫。

shared_ptr<double> pd;
double *p_reg = new double;
pd = p_reg; //不允許
pd = shared_ptr<double>(new double); //允許
shared_ptr<double> pshared = p_reg; // 不允許
shared_ptr<double> pshared(p_reg); //允許

一、為什麼C++11要摒棄auto_ptr:

void main()
{
	/*auto_ptr<string> ps(new string("hello"));
	auto_ptr<string> vocation;
	ps = vocation;*/
	auto_ptr<string> film[5] =
	{
		auto_ptr<string>(new string("1")),
		auto_ptr<string>(new string("2")),
		auto_ptr<string>(new string("3")),
		auto_ptr<string>(new string("4")),
		auto_ptr<string>(new string("5"))
	};
	auto_ptr<string> pwin;
	pwin = film[2];
	for (int i = 0; i < 5; i++)
		cout << *film[i] << endl;
	system("pause");
	return;
}

程式奔潰原因在於,程式將所有權從film[2]轉讓給pwin,導致film[2]不再引用該字串,當列印film[2]指向的字串時,發現這是一個空指標。

解決方法:把auto_ptr改成shared_ptr,

shared_ptr的原理是採用引用計數,當pwin和film[2]指向同一個物件時,引用計數從1增加到2,在程式末尾,後宣告的pwin先呼叫其解構函式,該解構函式將引用計數-1,變成了1,然後shared_ptr陣列成員釋放,將引用計數從1變成0,釋放以前分配的空間。

二、為何unique_ptr優於auto_ptr

如果出現下列語句

unique_ptr<string> ps3(new string("auto"));
unique_ptr<string> ps4;
ps3 = ps4;  #編譯器認為該句非法

unique_ptr可防止p3和p4的解構函式釋放同一個物件。

但是如果像下列語句

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

void main()
{
    unique_ptr<string> ps;
    ps = demo("Uniquely special");
}

demo函式返回一個臨時的unique_ptr,ps接管了temp的所有權(其實呼叫了unique_ptr的拷貝構造),因為demo生成的是右值

出了這語句temp就被銷燬。

總之,編譯器支援這種語法,如果源unique_ptr將存在一段時間,則編譯器禁止這樣做。

unique_ptr相比於auto_ptr還有另外一個優點,它有一個可用於陣列的變體。

模板auto_ptr支援new和delete但是不支援new[]和delete[],unique_ptr卻可以。

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

三、如何選擇智慧指標

如果程式要使用多個指向同一個物件的指標,應選擇shared_ptr,如果你的編譯器沒有提供,可以選擇boost庫中的shared_ptr

相反,可以是用unique_ptr,如果編譯器沒有提供,可以考慮使用boost庫中的scoped_ptr,它與unique_ptr類似。

最好不要用auto_ptr。