欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文掌握C++ 智能指針全部用法

 更新時(shí)間:2021年08月04日 11:01:24   作者:cpp_learner  
學(xué)習(xí)智能指針有很多好處,可以幫我們C++程序員管理動(dòng)態(tài)分配的內(nèi)存的,它會(huì)幫助我們自動(dòng)釋放new出來(lái)的內(nèi)存,從而避免內(nèi)存泄漏,感興趣的朋友跟隨小編一起看看吧

為什么要學(xué)習(xí)智能指針?
咳咳,這個(gè)問(wèn)題不是問(wèn)大家的,是詢問(wèn)我自己的!
我依稀記得剛離校出來(lái)找實(shí)習(xí)工作那會(huì),去面試一份工作,其中有一個(gè)環(huán)節(jié)需要答題;有一道題目就是問(wèn)什么是智能指針?臥槽?當(dāng)時(shí)我就懵逼,智能指針我壓根就沒(méi)有聽(tīng)說(shuō)過(guò)…
最后,面試的這份工作理所應(yīng)當(dāng)?shù)狞S了。
差不多是一年前左右吧,現(xiàn)在趁有閑余時(shí)間,學(xué)習(xí)一下智能指針,豐富一下自己!

一、為什么要使用智能指針

一句話帶過(guò):智能指針就是幫我們C++程序員管理動(dòng)態(tài)分配的內(nèi)存的,它會(huì)幫助我們自動(dòng)釋放new出來(lái)的內(nèi)存,從而避免內(nèi)存泄漏!

如下例子就是內(nèi)存泄露的例子:

#include <iostream>
#include <string>
#include <memory>

using namespace std;


// 動(dòng)態(tài)分配內(nèi)存,沒(méi)有釋放就return
void memoryLeak1() {
	string *str = new string("動(dòng)態(tài)分配內(nèi)存!");
	return;
}

// 動(dòng)態(tài)分配內(nèi)存,雖然有些釋放內(nèi)存的代碼,但是被半路截胡return了
int memoryLeak2() {
	string *str = new string("內(nèi)存泄露!");

	// ...此處省略一萬(wàn)行代碼

	// 發(fā)生某些異常,需要結(jié)束函數(shù)
	if (1) {
		return -1;
	}

	delete str;	// 雖然寫(xiě)了釋放內(nèi)存的代碼,但是遭到函數(shù)中段返回,使得指針沒(méi)有得到釋放
	return 1;
}


int main(void) {

	memoryLeak1();

	memoryLeak2();

	return 0;
} 

memoryLeak1函數(shù)中,new了一個(gè)字符串指針,但是沒(méi)有delete就已經(jīng)return結(jié)束函數(shù)了,導(dǎo)致內(nèi)存沒(méi)有被釋放,內(nèi)存泄露!
memoryLeak2函數(shù)中,new了一個(gè)字符串指針,雖然在函數(shù)末尾有些釋放內(nèi)存的代碼delete str,但是在delete之前就已經(jīng)return了,所以內(nèi)存也沒(méi)有被釋放,內(nèi)存泄露!

使用指針,我們沒(méi)有釋放,就會(huì)造成內(nèi)存泄露。但是我們使用普通對(duì)象卻不會(huì)!

思考:如果我們分配的動(dòng)態(tài)內(nèi)存都交由有生命周期的對(duì)象來(lái)處理,那么在對(duì)象過(guò)期時(shí),讓它的析構(gòu)函數(shù)刪除指向的內(nèi)存,這看似是一個(gè) very nice 的方案?

智能指針就是通過(guò)這個(gè)原理來(lái)解決指針自動(dòng)釋放的問(wèn)題!

  1. C++98 提供了 auto_ptr 模板的解決方案
  2. C++11 增加unique_ptr、shared_ptr 和weak_ptr

二、auto_ptr

auto_ptr 是c++ 98定義的智能指針模板,其定義了管理指針的對(duì)象,可以將new 獲得(直接或間接)的地址賦給這種對(duì)象。當(dāng)對(duì)象過(guò)期時(shí),其析構(gòu)函數(shù)將使用delete 來(lái)釋放內(nèi)存!

用法:
頭文件: #include < memory >
用 法: auto_ptr<類(lèi)型> 變量名(new 類(lèi)型)

例 如:
auto_ptr< string > str(new string(“我要成為大牛~ 變得很牛逼!”));
auto_ptr<vector< int >> av(new vector< int >());
auto_ptr< int > array(new int[10]);

例:
我們先定義一個(gè)類(lèi),類(lèi)的構(gòu)造函數(shù)和析構(gòu)函數(shù)都輸出一個(gè)字符串用作提示!
定義一個(gè)私有成員變量,賦值20.
再定義一個(gè)私有成員方法用于返回這個(gè)私有成員變量。

class Test {
public:
	Test() { cout << "Test的構(gòu)造函數(shù)..." << endl; }
	~Test() { cout << "Test的析構(gòu)函數(shù)..." << endl; }

	int getDebug() { return this->debug; }

private:
	int debug = 20;
};

當(dāng)我們直接new這個(gè)類(lèi)的對(duì)象,卻沒(méi)有釋放時(shí)。。。

int main(void) {
	Test *test = new Test;

	return 0;
} 

在這里插入圖片描述

可以看到,只是打印了構(gòu)造函數(shù)這個(gè)字符串,而析構(gòu)函數(shù)的字符卻沒(méi)有被打印,說(shuō)明并沒(méi)有調(diào)用析構(gòu)函數(shù)!這就導(dǎo)致了內(nèi)存泄露!
解決內(nèi)存泄露的辦法,要么手動(dòng)delete,要么使用智能指針!

使用智能指針:

// 定義智能指針
auto_ptr<Test> test(new Test);

智能指針可以像普通指針那樣使用:

cout << "test->debug:" << test->getDebug() << endl;
cout << "(*test).debug:" << (*test).getDebug() << endl;

這時(shí)再試試:

int main(void) {

	//Test *test = new Test;
	auto_ptr<Test> test(new Test);

	cout << "test->debug:" << test->getDebug() << endl;
	cout << "(*test).debug:" << (*test).getDebug() << endl;

	return 0;
} 

在這里插入圖片描述

自動(dòng)調(diào)用了析構(gòu)函數(shù)。
為什么智能指針可以像普通指針那樣使用???
因?yàn)槠淅锩嬷剌d了 * 和 -> 運(yùn)算符, * 返回普通對(duì)象,而 -> 返回指針對(duì)象。

在這里插入圖片描述

具體原因不用深究,只需知道他為什么可以這樣操作就像!
函數(shù)中返回的是調(diào)用get()方法返回的值,那么這個(gè)get()是什么呢?

智能指針的三個(gè)常用函數(shù):

get() 獲取智能指針托管的指針地址

// 定義智能指針
auto_ptr<Test> test(new Test);

Test *tmp = test.get();		// 獲取指針?lè)祷?
cout << "tmp->debug:" << tmp->getDebug() << endl;

但我們一般不會(huì)這樣使用,因?yàn)槎伎梢灾苯邮褂弥悄苤羔樔ゲ僮?,除非有一些特殊情況。
函數(shù)原型:

_NODISCARD _Ty * get() const noexcept
{	// return wrapped pointer
	return (_Myptr);
}

release() 取消智能指針對(duì)動(dòng)態(tài)內(nèi)存的托管

// 定義智能指針
auto_ptr<Test> test(new Test);

Test *tmp2 = test.release();	// 取消智能指針對(duì)動(dòng)態(tài)內(nèi)存的托管
delete tmp2;	// 之前分配的內(nèi)存需要自己手動(dòng)釋放

也就是智能指針不再對(duì)該指針進(jìn)行管理,改由管理員進(jìn)行管理!
函數(shù)原型:

_Ty * release() noexcept
{	// return wrapped pointer and give up ownership
	_Ty * _Tmp = _Myptr;
	_Myptr = nullptr;
	return (_Tmp);
}

reset() 重置智能指針托管的內(nèi)存地址,如果地址不一致,原來(lái)的會(huì)被析構(gòu)掉

// 定義智能指針
auto_ptr<Test> test(new Test);

test.reset();			// 釋放掉智能指針托管的指針內(nèi)存,并將其置NULL

test.reset(new Test());	// 釋放掉智能指針托管的指針內(nèi)存,并將參數(shù)指針取代之

reset函數(shù)會(huì)將參數(shù)的指針(不指定則為NULL),與托管的指針比較,如果地址不一致,那么就會(huì)析構(gòu)掉原來(lái)托管的指針,然后使用參數(shù)的指針替代之。然后智能指針就會(huì)托管參數(shù)的那個(gè)指針了。
函數(shù)原型:

void reset(_Ty * _Ptr = nullptr)
{	// destroy designated object and store new pointer
	if (_Ptr != _Myptr)
		delete _Myptr;
	_Myptr = _Ptr;
}

使用建議:

盡可能不要將auto_ptr 變量定義為全局變量或指針;

// 沒(méi)有意義,全局變量也是一樣
auto_ptr<Test> *tp = new auto_ptr<Test>(new Test);	

除非自己知道后果,不要把a(bǔ)uto_ptr 智能指針賦值給同類(lèi)型的另外一個(gè) 智能指針;

auto_ptr<Test> t1(new Test);
auto_ptr<Test> t2(new Test);
t1 = t2;	// 不要這樣操作...

C++11 后auto_ptr 已經(jīng)被“拋棄”,已使用unique_ptr替代!C++11后不建議使用auto_ptr。

auto_ptr 被C++11拋棄的主要原因

1). 復(fù)制或者賦值都會(huì)改變資源的所有權(quán)

// auto_ptr 被C++11拋棄的主要原因
auto_ptr<string> p1(new string("I'm Li Ming!"));
auto_ptr<string> p2(new string("I'm age 22."));

cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;

// p2賦值給p1后,首先p1會(huì)先將自己原先托管的指針釋放掉,然后接收托管p2所托管的指針,
// 然后p2所托管的指針制NULL,也就是p1托管了p2托管的指針,而p2放棄了托管。
p1 = p2;	
cout << "p1 = p2 賦值后:" << endl;
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;

在這里插入圖片描述

2). 在STL容器中使用auto_ptr存在著重大風(fēng)險(xiǎn),因?yàn)槿萜鲀?nèi)的元素必須支持可復(fù)制和可賦值

vector<auto_ptr<string>> vec;
auto_ptr<string> p3(new string("I'm P3"));
auto_ptr<string> p4(new string("I'm P4"));

// 必須使用std::move修飾成右值,才可以進(jìn)行插入容器中
vec.push_back(std::move(p3));
vec.push_back(std::move(p4));

cout << "vec.at(0):" <<  *vec.at(0) << endl;
cout << "vec[1]:" <<  *vec[1] << endl;


// 風(fēng)險(xiǎn)來(lái)了:
vec[0] = vec[1];	// 如果進(jìn)行賦值,問(wèn)題又回到了上面一個(gè)問(wèn)題中。
cout << "vec.at(0):" << *vec.at(0) << endl;
cout << "vec[1]:" << *vec[1] << endl;

訪問(wèn)越界了!

在這里插入圖片描述

3). 不支持對(duì)象數(shù)組的內(nèi)存管理

auto_ptr<int[]> array(new int[5]);	// 不能這樣定義

在這里插入圖片描述

所以,C++11用更嚴(yán)謹(jǐn)?shù)膗nique_ptr 取代了auto_ptr!

測(cè)試代碼:

#include <iostream>
#include <string>
#include <memory>
#include <vector>

using namespace std;

class Test {
public:
	Test() { cout << "Test的構(gòu)造函數(shù)..." << endl; }
	~Test() { cout << "Test的析構(gòu)函數(shù)..." << endl; }

	int getDebug() { return this->debug; }

private:
	int debug = 20;
};

// 不要定義為全局變量,沒(méi)有意義
//auto_ptr<Test> test(new Test);

void memoryLeak1() {
	//Test *test = new Test;

	// 定義智能指針
	auto_ptr<Test> test(new Test);
	
	cout << "test->debug:" << test->getDebug() << endl;
	cout << "(*test).debug:" << (*test).getDebug() << endl;


	// get方法
	Test *tmp = test.get();		// 獲取指針?lè)祷?
	cout << "tmp->debug:" << tmp->getDebug() << endl;


	// release方法
	Test *tmp2 = test.release();	// 取消智能指針對(duì)動(dòng)態(tài)內(nèi)存的托管
	delete tmp2;	// 之前分配的內(nèi)存需要自己手動(dòng)釋放


	// reset方法:重置智能指針托管的內(nèi)存地址,如果地址不一致,原來(lái)的會(huì)被析構(gòu)掉
	test.reset();			// 釋放掉智能指針托管的指針內(nèi)存,并將其置NULL
	test.reset(new Test());	// 釋放掉智能指針托管的指針內(nèi)存,并將參數(shù)指針取代之


	// 忠告:不要將智能指針定義為指針
	//auto_ptr<Test> *tp = new auto_ptr<Test>(new Test);

	// 忠告:不要定義指向智能指針對(duì)象的指針變量
	//auto_ptr<Test> t1(new Test);
	//auto_ptr<Test> t2(new Test);
	//t1 = t2;

	return;
}

int memoryLeak2() {
	//Test *test = new Test();

	// 定義智能指針
	auto_ptr<Test> test(new Test);

	// ...此處省略一萬(wàn)行代碼

	// 發(fā)生某些異常,需要結(jié)束函數(shù)
	if (1) {
		return -1;
	}

	//delete test;
	return 1;
}


int main1(void) {

	//memoryLeak1();

	//memoryLeak2();

	//Test *test = new Test;
	//auto_ptr<Test> test(new Test);

	//cout << "test->debug:" << test->getDebug() << endl;
	//cout << "(*test).debug:" << (*test).getDebug() << endl;


	 auto_ptr 被C++11拋棄的主要原因
	//auto_ptr<string> p1(new string("I'm Li Ming!"));
	//auto_ptr<string> p2(new string("I'm age 22."));
	//
	//cout << "p1:" << p1.get() << endl;
	//cout << "p2:" << p2.get() << endl;

	//p1 = p2;
	//cout << "p1 = p2 賦值后:" << endl;
	//cout << "p1:" << p1.get() << endl;
	//cout << "p2:" << p2.get() << endl;



	// 弊端2.在STL容器中使用auto_ptr存在著重大風(fēng)險(xiǎn),因?yàn)槿萜鲀?nèi)的元素必須支持可復(fù)制
	vector<auto_ptr<string>> vec;
	auto_ptr<string> p3(new string("I'm P3"));
	auto_ptr<string> p4(new string("I'm P4"));

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

	cout << "vec.at(0):" <<  *vec.at(0) << endl;
	cout << "vec[1]:" <<  *vec[1] << endl;


	// 風(fēng)險(xiǎn)來(lái)了:
	vec[0] = vec[1];
	cout << "vec.at(0):" << *vec.at(0) << endl;
	cout << "vec[1]:" << *vec[1] << endl;


	// 弊端3.不支持對(duì)象數(shù)組的內(nèi)存管理
	//auto_ptr<int[]> array(new int[5]);	// 不能這樣定義
	return 0;
} 

三、unique_ptr

auto_ptr是用于C++11之前的智能指針。由于 auto_ptr 基于排他所有權(quán)模式:兩個(gè)指針不能指向同一個(gè)資源,復(fù)制或賦值都會(huì)改變資源的所有權(quán)。auto_ptr 主要有三大問(wèn)題:

  • 復(fù)制和賦值會(huì)改變資源的所有權(quán),不符合人的直覺(jué)。
  • 在 STL 容器中使用auto_ptr存在重大風(fēng)險(xiǎn),因?yàn)槿萜鲀?nèi)的元素必需支持可復(fù)制(copy constructable)和可賦值(assignable)。
  • 不支持對(duì)象數(shù)組的操作

以上問(wèn)題已經(jīng)在上面體現(xiàn)出來(lái)了,下面將使用unique_ptr解決這些問(wèn)題。

所以,C++11用更嚴(yán)謹(jǐn)?shù)膗nique_ptr 取代了auto_ptr!

unique_ptr 和 auto_ptr用法幾乎一樣,除了一些特殊。

unique_ptr特性

  1. 基于排他所有權(quán)模式:兩個(gè)指針不能指向同一個(gè)資源
  2. 無(wú)法進(jìn)行左值unique_ptr復(fù)制構(gòu)造,也無(wú)法進(jìn)行左值復(fù)制賦值操作,但允許臨時(shí)右值賦值構(gòu)造和賦值
  3. 保存指向某個(gè)對(duì)象的指針,當(dāng)它本身離開(kāi)作用域時(shí)會(huì)自動(dòng)釋放它指向的對(duì)象。
  4. 在容器中保存指針是安全的

A. 無(wú)法進(jìn)行左值復(fù)制賦值操作,但允許臨時(shí)右值賦值構(gòu)造和賦值

unique_ptr<string> p1(new string("I'm Li Ming!"));
unique_ptr<string> p2(new string("I'm age 22."));
	
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;

p1 = p2;					// 禁止左值賦值
unique_ptr<string> p3(p2);	// 禁止左值賦值構(gòu)造

unique_ptr<string> p3(std::move(p1));
p1 = std::move(p2);	// 使用move把左值轉(zhuǎn)成右值就可以賦值了,效果和auto_ptr賦值一樣

cout << "p1 = p2 賦值后:" << endl;
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;

在這里插入圖片描述

運(yùn)行截圖:

在這里插入圖片描述

B. 在 STL 容器中使用unique_ptr,不允許直接賦值

vector<unique_ptr<string>> vec;
unique_ptr<string> p3(new string("I'm P3"));
unique_ptr<string> p4(new string("I'm P4"));

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

cout << "vec.at(0):" << *vec.at(0) << endl;
cout << "vec[1]:" << *vec[1] << endl;

vec[0] = vec[1];	/* 不允許直接賦值 */
vec[0] = std::move(vec[1]);		// 需要使用move修飾,使得程序員知道后果

cout << "vec.at(0):" << *vec.at(0) << endl;
cout << "vec[1]:" << *vec[1] << endl;

在這里插入圖片描述

當(dāng)然,運(yùn)行后是直接報(bào)錯(cuò)的,因?yàn)関ec[1]已經(jīng)是NULL了,再繼續(xù)訪問(wèn)就越界了。

C. 支持對(duì)象數(shù)組的內(nèi)存管理

// 會(huì)自動(dòng)調(diào)用delete [] 函數(shù)去釋放內(nèi)存
unique_ptr<int[]> array(new int[5]);	// 支持這樣定義

除了上面ABC三項(xiàng)外,unique_ptr的其余用法都與auto_ptr用法一致。

構(gòu)造

class Test {
public:
	Test() { cout << "Test的構(gòu)造函數(shù)..." << endl; }
	~Test() { cout << "Test的析構(gòu)函數(shù)..." << endl; }

	void doSomething() { cout << "do something......" << endl; }
};


// 自定義一個(gè)內(nèi)存釋放其
class DestructTest {
	public:
	void operator()(Test *pt) {
		pt->doSomething();
		delete pt;
	}
};

// unique_ptr<T> up; 空的unique_ptr,可以指向類(lèi)型為T(mén)的對(duì)象
unique_ptr<Test> t1;

// unique_ptr<T> up1(new T());	定義unique_ptr,同時(shí)指向類(lèi)型為T(mén)的對(duì)象
unique_ptr<Test> t2(new Test);

// unique_ptr<T[]> up;	空的unique_ptr,可以指向類(lèi)型為T(mén)[的數(shù)組對(duì)象
unique_ptr<int[]> t3;

// unique_ptr<T[]> up1(new T[]);	定義unique_ptr,同時(shí)指向類(lèi)型為T(mén)的數(shù)組對(duì)象
unique_ptr<int[]> t4(new int[5]);

// unique_ptr<T, D> up();	空的unique_ptr,接受一個(gè)D類(lèi)型的刪除器D,使用D釋放內(nèi)存
unique_ptr<Test, DestructTest> t5;

// unique_ptr<T, D> up(new T());	定義unique_ptr,同時(shí)指向類(lèi)型為T(mén)的對(duì)象,接受一個(gè)D類(lèi)型的刪除器D,使用刪除器D來(lái)釋放內(nèi)存
unique_ptr<Test, DestructTest> t6(new Test);

賦值

unique_ptr<Test> t7(new Test);
unique_ptr<Test> t8(new Test);
t7 = std::move(t8);	// 必須使用移動(dòng)語(yǔ)義,結(jié)果,t7的內(nèi)存釋放,t8的內(nèi)存交給t7管理
t7->doSomething();

主動(dòng)釋放對(duì)象

unique_ptr<Test> t9(new Test);
t9 = NULL;
t9 = nullptr;
t9.reset();

放棄對(duì)象的控制權(quán)

Test *t10 = t9.release();

重置

t9.reset(new Test);

auto_ptr 與 unique_ptr智能指針的內(nèi)存管理陷阱

auto_ptr<string> p1;
string *str = new string("智能指針的內(nèi)存管理陷阱");
p1.reset(str);	// p1托管str指針
{
	auto_ptr<string> p2;
	p2.reset(str);	// p2接管str指針時(shí),會(huì)先取消p1的托管,然后再對(duì)str的托管
}

// 此時(shí)p1已經(jīng)沒(méi)有托管內(nèi)容指針了,為NULL,在使用它就會(huì)內(nèi)存報(bào)錯(cuò)!
cout << "str:" << *p1 << endl;

在這里插入圖片描述

這是由于auto_ptr 與 unique_ptr的排他性所導(dǎo)致的!
為了解決這樣的問(wèn)題,我們可以使用shared_ptr指針指針!


四、shared_ptr

熟悉了unique_ptr 后,其實(shí)我們發(fā)現(xiàn)unique_ptr 這種排他型的內(nèi)存管理并不能適應(yīng)所有情況,有很大的局限!如果需要多個(gè)指針變量共享怎么辦?

如果有一種方式,可以記錄引用特定內(nèi)存對(duì)象的智能指針數(shù)量,當(dāng)復(fù)制或拷貝時(shí),引用計(jì)數(shù)加1,當(dāng)智能指針析構(gòu)時(shí),引用計(jì)數(shù)減1,如果計(jì)數(shù)為零,代表已經(jīng)沒(méi)有指針指向這塊內(nèi)存,那么我們就釋放它!這就是 shared_ptr 采用的策略!

在這里插入圖片描述

例:

class Person {
public:
	Person(int v) {
		this->no = v;
		cout << "構(gòu)造函數(shù) \t no = " << this->no << endl;
	}

	~Person() {
		cout << "析構(gòu)函數(shù) \t no = " << this->no << endl;
	}

private:
	int no;
};

// 仿函數(shù),內(nèi)存刪除
class DestructPerson {
public:
	void operator() (Person *pt) {
		cout << "DestructPerson..." << endl;
		delete pt;
	}
};

引用計(jì)數(shù)的使用

調(diào)用use_count函數(shù)可以獲得當(dāng)前托管指針的引用計(jì)數(shù)。

shared_ptr<Person> sp1;

shared_ptr<Person> sp2(new Person(2));

// 獲取智能指針管控的共享指針的數(shù)量	use_count():引用計(jì)數(shù)
cout << "sp1	use_count() = " << sp1.use_count() << endl;
cout << "sp2	use_count() = " << sp2.use_count() << endl << endl;

// 共享
sp1 = sp2;

cout << "sp1	use_count() = " << sp1.use_count() << endl;
cout << "sp2	use_count() = " << sp2.use_count() << endl << endl;

shared_ptr<Person> sp3(sp1);
cout << "sp1	use_count() = " << sp1.use_count() << endl;
cout << "sp2	use_count() = " << sp2.use_count() << endl;
cout << "sp2	use_count() = " << sp3.use_count() << endl << endl;

如上代碼,sp1 = sp2; 和 shared_ptr< Person > sp3(sp1);就是在使用引用計(jì)數(shù)了。

sp1 = sp2; --> sp1和sp2共同托管同一個(gè)指針,所以他們的引用計(jì)數(shù)為2;
shared_ptr< Person > sp3(sp1); --> sp1和sp2和sp3共同托管同一個(gè)指針,所以他們的引用計(jì)數(shù)為3;

在這里插入圖片描述

構(gòu)造

1). shared_ptr< T > sp1; 空的shared_ptr,可以指向類(lèi)型為T(mén)的對(duì)象

shared_ptr<Person> sp1;
Person *person1 = new Person(1);
sp1.reset(person1);	// 托管person1

2). shared_ptr< T > sp2(new T()); 定義shared_ptr,同時(shí)指向類(lèi)型為T(mén)的對(duì)象

shared_ptr<Person> sp2(new Person(2));
shared_ptr<Person> sp3(sp1);

3). shared_ptr<T[]> sp4; 空的shared_ptr,可以指向類(lèi)型為T(mén)[]的數(shù)組對(duì)象 C++17后支持

shared_ptr<Person[]> sp4;

4). shared_ptr<T[]> sp5(new T[] { … }); 指向類(lèi)型為T(mén)的數(shù)組對(duì)象 C++17后支持

shared_ptr<Person[]> sp5(new Person[5] { 3, 4, 5, 6, 7 });

5). shared_ptr< T > sp6(NULL, D()); //空的shared_ptr,接受一個(gè)D類(lèi)型的刪除器,使用D釋放內(nèi)存

shared_ptr<Person> sp6(NULL, DestructPerson());

6). shared_ptr< T > sp7(new T(), D()); //定義shared_ptr,指向類(lèi)型為T(mén)的對(duì)象,接受一個(gè)D類(lèi)型的刪除器,使用D刪除器來(lái)釋放內(nèi)存

shared_ptr<Person> sp7(new Person(8), DestructPerson());

初始化

1). 方式一:構(gòu)造函數(shù)

shared_ptr<int> up1(new int(10));  // int(10) 的引用計(jì)數(shù)為1
shared_ptr<int> up2(up1);  // 使用智能指針up1構(gòu)造up2, 此時(shí)int(10) 引用計(jì)數(shù)為2

2). 方式二:使用make_shared 初始化對(duì)象,分配內(nèi)存效率更高(推薦使用)
make_shared函數(shù)的主要功能是在動(dòng)態(tài)內(nèi)存中分配一個(gè)對(duì)象并初始化它,返回指向此對(duì)象的shared_ptr; 用法:
make_shared<類(lèi)型>(構(gòu)造類(lèi)型對(duì)象需要的參數(shù)列表);

shared_ptr<int> up3 = make_shared<int>(2); // 多個(gè)參數(shù)以逗號(hào)','隔開(kāi),最多接受十個(gè)
shared_ptr<string> up4 = make_shared<string>("字符串");
shared_ptr<Person> up5 = make_shared<Person>(9);

賦值

shared_ptrr<int> up1(new int(10));  // int(10) 的引用計(jì)數(shù)為1
shared_ptr<int> up2(new int(11));   // int(11) 的引用計(jì)數(shù)為1
up1 = up2;	// int(10) 的引用計(jì)數(shù)減1,計(jì)數(shù)歸零內(nèi)存釋放,up2共享int(11)給up1, int(11)的引用計(jì)數(shù)為2

主動(dòng)釋放對(duì)象

shared_ptrr<int> up1(new int(10));
up1 = nullptr ;	// int(10) 的引用計(jì)數(shù)減1,計(jì)數(shù)歸零內(nèi)存釋放 
// 或
up1 = NULL; // 作用同上 

重置
p.reset() ; 將p重置為空指針,所管理對(duì)象引用計(jì)數(shù) 減1
p.reset(p1); 將p重置為p1(的值),p 管控的對(duì)象計(jì)數(shù)減1,p接管對(duì)p1指針的管控
p.reset(p1,d); 將p重置為p1(的值),p 管控的對(duì)象計(jì)數(shù)減1并使用d作為刪除器
p1是一個(gè)指針!

交換
p1 和 p2 是智能指針

std::swap(p1,p2); // 交換p1 和p2 管理的對(duì)象,原對(duì)象的引用計(jì)數(shù)不變
p1.swap(p2);    // 交換p1 和p2 管理的對(duì)象,原對(duì)象的引用計(jì)數(shù)不變

shared_ptr使用陷阱

shared_ptr作為被管控的對(duì)象的成員時(shí),小心因循環(huán)引用造成無(wú)法釋放資源!

如下代碼:
Boy類(lèi)中有Girl的智能指針;
Girl類(lèi)中有Boy的智能指針;
當(dāng)他們交叉互相持有對(duì)方的管理對(duì)象時(shí)…

#include <iostream>
#include <string>
#include <memory>

using namespace std;

class Girl;

class Boy {
public:
	Boy() {
		cout << "Boy 構(gòu)造函數(shù)" << endl;
	}

	~Boy() {
		cout << "~Boy 析構(gòu)函數(shù)" << endl;
	}

	void setGirlFriend(shared_ptr<Girl> _girlFriend) {
		this->girlFriend = _girlFriend;
	}

private:
	shared_ptr<Girl> girlFriend;
};

class Girl {
public:
	Girl() {
		cout << "Girl 構(gòu)造函數(shù)" << endl;
	}

	~Girl() {
		cout << "~Girl 析構(gòu)函數(shù)" << endl;
	}

	void setBoyFriend(shared_ptr<Boy> _boyFriend) {
		this->boyFriend = _boyFriend;
	}

private:
	shared_ptr<Boy> boyFriend;
};


void useTrap() {
	shared_ptr<Boy> spBoy(new Boy());
	shared_ptr<Girl> spGirl(new Girl());

	// 陷阱用法
	spBoy->setGirlFriend(spGirl);
	spGirl->setBoyFriend(spBoy);
	// 此時(shí)boy和girl的引用計(jì)數(shù)都是2
}


int main(void) {
	useTrap();

	system("pause");
	return 0;
}

運(yùn)行截圖:

在這里插入圖片描述

可以看出,程序結(jié)束了,但是并沒(méi)有釋放內(nèi)存,這是為什么呢???

如下圖:
當(dāng)我們執(zhí)行useTrap函數(shù)時(shí),注意,是沒(méi)有結(jié)束此函數(shù),boy和girl指針其實(shí)是被兩個(gè)智能指針托管的,所以他們的引用計(jì)數(shù)是2

在這里插入圖片描述

useTrap函數(shù)結(jié)束后,函數(shù)中定義的智能指針被清掉,boy和girl指針的引用計(jì)數(shù)減1,還剩下1,對(duì)象中的智能指針還是托管他們的,所以函數(shù)結(jié)束后沒(méi)有將boy和gilr指針釋放的原因就是于此。

在這里插入圖片描述

所以在使用shared_ptr智能指針時(shí),要注意避免對(duì)象交叉使用智能指針的情況! 否則會(huì)導(dǎo)致內(nèi)存泄露!

當(dāng)然,這也是有辦法解決的,那就是使用weak_ptr弱指針。

針對(duì)上面的情況,還講一下另一種情況。如果是單方獲得管理對(duì)方的共享指針,那么這樣著是可以正常釋放掉的!
例如:

void useTrap() {
	shared_ptr<Boy> spBoy(new Boy());
	shared_ptr<Girl> spGirl(new Girl());

	// 單方獲得管理
	//spBoy->setGirlFriend(spGirl);
	spGirl->setBoyFriend(spBoy);	
}

在這里插入圖片描述

反過(guò)來(lái)也是一樣的!

這是什么原理呢?

  • 首先釋放spBoy,但是因?yàn)間irl對(duì)象里面的智能指針還托管著boy,boy的引用計(jì)數(shù)為2,所以釋放spBoy時(shí),引用計(jì)數(shù)減1,boy的引用計(jì)數(shù)為1;
  • 在釋放spGirl,girl的引用計(jì)數(shù)減1,為零,開(kāi)始釋放girl的內(nèi)存,因?yàn)間irl里面還包含有托管boy的智能指針對(duì)象,所以也會(huì)進(jìn)行boyFriend的內(nèi)存釋放,boy的引用計(jì)數(shù)減1,為零,接著開(kāi)始釋放boy的內(nèi)存。最終所有的內(nèi)存都釋放了。

五、weak_ptr

weak_ptr 設(shè)計(jì)的目的是為配合 shared_ptr 而引入的一種智能指針來(lái)協(xié)助 shared_ptr 工作, 它只可以從一個(gè) shared_ptr 或另一個(gè) weak_ptr 對(duì)象構(gòu)造, 它的構(gòu)造和析構(gòu)不會(huì)引起引用記數(shù)的增加或減少。 同時(shí)weak_ptr 沒(méi)有重載*和->但可以使用 lock 獲得一個(gè)可用的 shared_ptr 對(duì)象。

  1. 弱指針的使用;

weak_ptr wpGirl_1; // 定義空的弱指針
weak_ptr wpGirl_2(spGirl); // 使用共享指針構(gòu)造
wpGirl_1 = spGirl; // 允許共享指針賦值給弱指針

  1. 弱指針也可以獲得引用計(jì)數(shù);

wpGirl_1.use_count()

  1. 弱指針不支持 * 和 -> 對(duì)指針的訪問(wèn);

在這里插入圖片描述

在必要的使用可以轉(zhuǎn)換成共享指針 lock();

shared_ptr<Girl> sp_girl;
sp_girl = wpGirl_1.lock();

// 使用完之后,再將共享指針置NULL即可
sp_girl = NULL;

使用代碼:

shared_ptr<Boy> spBoy(new Boy());
shared_ptr<Girl> spGirl(new Girl());

// 弱指針的使用
weak_ptr<Girl> wpGirl_1;			// 定義空的弱指針
weak_ptr<Girl> wpGirl_2(spGirl);	// 使用共享指針構(gòu)造
wpGirl_1 = spGirl;					// 允許共享指針賦值給弱指針

cout << "spGirl \t use_count = " << spGirl.use_count() << endl;
cout << "wpGirl_1 \t use_count = " << wpGirl_1.use_count() << endl;

	
// 弱指針不支持 * 和 -> 對(duì)指針的訪問(wèn)
/*wpGirl_1->setBoyFriend(spBoy);
(*wpGirl_1).setBoyFriend(spBoy);*/

// 在必要的使用可以轉(zhuǎn)換成共享指針
shared_ptr<Girl> sp_girl;
sp_girl = wpGirl_1.lock();

cout << sp_girl.use_count() << endl;
// 使用完之后,再將共享指針置NULL即可
sp_girl = NULL;

當(dāng)然這只是一些使用上的小例子,具體用法如下:

請(qǐng)看Boy類(lèi)

#include <iostream>
#include <string>
#include <memory>

using namespace std;

class Girl;

class Boy {
public:
	Boy() {
		cout << "Boy 構(gòu)造函數(shù)" << endl;
	}

	~Boy() {
		cout << "~Boy 析構(gòu)函數(shù)" << endl;
	}

	void setGirlFriend(shared_ptr<Girl> _girlFriend) {
		this->girlFriend = _girlFriend;


		// 在必要的使用可以轉(zhuǎn)換成共享指針
		shared_ptr<Girl> sp_girl;
		sp_girl = this->girlFriend.lock();

		cout << sp_girl.use_count() << endl;
		// 使用完之后,再將共享指針置NULL即可
		sp_girl = NULL;
	}

private:
	weak_ptr<Girl> girlFriend;
};

class Girl {
public:
	Girl() {
		cout << "Girl 構(gòu)造函數(shù)" << endl;
	}

	~Girl() {
		cout << "~Girl 析構(gòu)函數(shù)" << endl;
	}

	void setBoyFriend(shared_ptr<Boy> _boyFriend) {
		this->boyFriend = _boyFriend;
	}

private:
	shared_ptr<Boy> boyFriend;
};


void useTrap() {
	shared_ptr<Boy> spBoy(new Boy());
	shared_ptr<Girl> spGirl(new Girl());

	spBoy->setGirlFriend(spGirl);
	spGirl->setBoyFriend(spBoy);
}


int main(void) {
	useTrap();

	system("pause");
	return 0;
}

在這里插入圖片描述

在類(lèi)中使用弱指針接管共享指針,在需要使用時(shí)就轉(zhuǎn)換成共享指針去使用即可!

自此問(wèn)題完美解決!


六、智能指針的使用陷阱

不要把一個(gè)原生指針給多個(gè)智能指針管理;

int *x = new int(10);
unique_ptr< int > up1(x);
unique_ptr< int > up2(x);
// 警告! 以上代碼使up1 up2指向同一個(gè)內(nèi)存,非常危險(xiǎn)
或以下形式:
up1.reset(x);
up2.reset(x);

記得使用u.release()的返回值;
在調(diào)用u.release()時(shí)是不會(huì)釋放u所指的內(nèi)存的,這時(shí)返回值就是對(duì)這塊內(nèi)存的唯一索引,如果沒(méi)有使用這個(gè)返回值釋放內(nèi)存或是保存起來(lái),這塊內(nèi)存就泄漏了.

禁止delete 智能指針get 函數(shù)返回的指針;
如果我們主動(dòng)釋放掉get 函數(shù)獲得的指針,那么智能 指針內(nèi)部的指針就變成野指針了,析構(gòu)時(shí)造成重復(fù)釋放,帶來(lái)嚴(yán)重后果!

禁止用任何類(lèi)型智能指針get 函數(shù)返回的指針去初始化另外一個(gè)智能指針!
shared_ptr< int > sp1(new int(10));
// 一個(gè)典型的錯(cuò)誤用法 shared_ptr< int > sp4(sp1.get());


七、總結(jié)

智能指針雖然使用起來(lái)很方便,但是要注意使用智能指針的一些陷阱,否則會(huì)造成嚴(yán)重的內(nèi)存報(bào)錯(cuò)或者內(nèi)存泄露等問(wèn)題!

到此這篇關(guān)于一文掌握C++ 智能指針全部用法的文章就介紹到這了,更多相關(guān)c++智能指針用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • c語(yǔ)言中&的用法示例代碼

    c語(yǔ)言中&的用法示例代碼

    這篇文章主要給大家介紹了關(guān)于c語(yǔ)言中&的用法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • vs2019創(chuàng)建WebService服務(wù)的實(shí)現(xiàn)

    vs2019創(chuàng)建WebService服務(wù)的實(shí)現(xiàn)

    這篇文章主要介紹了vs2019創(chuàng)建WebService服務(wù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • C++?Primer的變量和基本類(lèi)型詳解

    C++?Primer的變量和基本類(lèi)型詳解

    這篇文章主要為大家介紹了C++?Primer,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-01-01
  • 擴(kuò)展KMP算法(Extend KMP)

    擴(kuò)展KMP算法(Extend KMP)

    我們這里說(shuō)的KMP不是拿來(lái)放電影的(雖然我很喜歡這個(gè)軟件),而是一種算法。KMP算法是拿來(lái)處理字符串匹配的。今天我們談到的是對(duì)KMP算法的拓展
    2014-08-08
  • C++使用標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)事件和委托以及信號(hào)和槽機(jī)制

    C++使用標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)事件和委托以及信號(hào)和槽機(jī)制

    這篇文章主要為大家詳細(xì)介紹了C++如何使用標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)事件和委托以及信號(hào)和槽機(jī)制,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下
    2022-11-11
  • C語(yǔ)言玩轉(zhuǎn)指針之指針的高階玩法

    C語(yǔ)言玩轉(zhuǎn)指針之指針的高階玩法

    指針可以表示一個(gè)變更的地址,在計(jì)算機(jī)程序中,通常表示內(nèi)存地址,存儲(chǔ)數(shù)據(jù)的地址,下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言玩轉(zhuǎn)指針之指針的高階玩法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-10-10
  • 單線程會(huì)導(dǎo)致死鎖你知道嗎

    單線程會(huì)導(dǎo)致死鎖你知道嗎

    這篇文章主要為大家詳細(xì)介紹了單線程會(huì)不會(huì)導(dǎo)致死鎖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-02-02
  • VS2013安裝配置和使用Boost庫(kù)教程

    VS2013安裝配置和使用Boost庫(kù)教程

    這篇文章主要為大家詳細(xì)介紹了VS2013安裝配置和使用Boost庫(kù)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • c++ builder TreeView控件節(jié)點(diǎn)遍歷代碼

    c++ builder TreeView控件節(jié)點(diǎn)遍歷代碼

    這篇文章介紹了c++ builder TreeView控件節(jié)點(diǎn)遍歷代碼,有需要的朋友可以參考一下
    2013-09-09
  • C語(yǔ)言示例講解while循環(huán)語(yǔ)句的用法

    C語(yǔ)言示例講解while循環(huán)語(yǔ)句的用法

    在不少實(shí)際問(wèn)題中有許多具有規(guī)律性的重復(fù)操作,因此在程序中就需要重復(fù)執(zhí)行某些語(yǔ)句。一組被重復(fù)執(zhí)行的語(yǔ)句稱之為循環(huán)體,C語(yǔ)言while語(yǔ)句可以是單個(gè)語(yǔ)句,也可以是一個(gè)語(yǔ)句塊,其條件可以是任意表達(dá)式,true是任意非零值,當(dāng)條件為真時(shí),循環(huán)進(jìn)行迭代
    2022-06-06

最新評(píng)論