c++11新特性之智能指針
很多人談到c++,說(shuō)它特別難,可能有一部分就是因?yàn)閏++的內(nèi)存管理吧,不像java那樣有虛擬機(jī)動(dòng)態(tài)的管理內(nèi)存,在程序運(yùn)行過(guò)程中可能就會(huì)出現(xiàn)內(nèi)存泄漏,然而這種問(wèn)題其實(shí)都可以通過(guò)c++11引入的智能指針來(lái)解決,相反我還認(rèn)為這種內(nèi)存管理還是c++語(yǔ)言的優(yōu)勢(shì),因?yàn)楸M在掌握。
c++11引入了三種智能指針:
std::shared_ptr
std::weak_ptr
std::unique_ptr
shared_ptr
shared_ptr使用了引用計(jì)數(shù),每一個(gè)shared_ptr的拷貝都指向相同的內(nèi)存,每次拷貝都會(huì)觸發(fā)引用計(jì)數(shù)+1,每次生命周期結(jié)束析構(gòu)的時(shí)候引用計(jì)數(shù)-1,在最后一個(gè)shared_ptr析構(gòu)的時(shí)候,內(nèi)存才會(huì)釋放。
使用方法如下:
struct ClassWrapper {
ClassWrapper() {
cout << "construct" << endl;
data = new int[10];
}
~ClassWrapper() {
cout << "deconstruct" << endl;
if (data != nullptr) {
delete[] data;
}
}
void Print() {
cout << "print" << endl;
}
int* data;
};
void Func(std::shared_ptr<ClassWrapper> ptr) {
ptr->Print();
}
int main() {
auto smart_ptr = std::make_shared<ClassWrapper>();
auto ptr2 = smart_ptr; // 引用計(jì)數(shù)+1
ptr2->Print();
Func(smart_ptr); // 引用計(jì)數(shù)+1
smart_ptr->Print();
ClassWrapper *p = smart_ptr.get(); // 可以通過(guò)get獲取裸指針
p->Print();
return 0;
}
智能指針還可以自定義刪除器,在引用計(jì)數(shù)為0的時(shí)候自動(dòng)調(diào)用刪除器來(lái)釋放對(duì)象的內(nèi)存,代碼如下:
std::shared_ptr<int> ptr(new int, [](int *p){ delete p; });
關(guān)于shared_ptr有幾點(diǎn)需要注意:
? 不要用一個(gè)裸指針初始化多個(gè)shared_ptr,會(huì)出現(xiàn)double_free導(dǎo)致程序崩潰
? 通過(guò)shared_from_this()返回this指針,不要把this指針作為shared_ptr返回出來(lái),因?yàn)閠his指針本質(zhì)就是裸指針,通過(guò)this返回可能 會(huì)導(dǎo)致重復(fù)析構(gòu),不能把this指針交給智能指針管理。
class A {
shared_ptr<A> GetSelf() {
return shared_from_this();
// return shared_ptr<A>(this); 錯(cuò)誤,會(huì)導(dǎo)致double free
}
};
盡量使用make_shared,少用new。
不要delete get()返回來(lái)的裸指針。
不是new出來(lái)的空間要自定義刪除器。
要避免循環(huán)引用,循環(huán)引用導(dǎo)致內(nèi)存永遠(yuǎn)不會(huì)被釋放,造成內(nèi)存泄漏。
using namespace std;
struct A;
struct B;
struct A {
std::shared_ptr<B> bptr;
~A() {
cout << "A delete" << endl;
}
};
struct B {
std::shared_ptr<A> aptr;
~B() {
cout << "B delete" << endl;
}
};
int main() {
auto aaptr = std::make_shared<A>();
auto bbptr = std::make_shared<B>();
aaptr->bptr = bbptr;
bbptr->aptr = aaptr;
return 0;
}
上面代碼,產(chǎn)生了循環(huán)引用,導(dǎo)致aptr和bptr的引用計(jì)數(shù)為2,離開(kāi)作用域后aptr和bptr的引用計(jì)數(shù)-1,但是永遠(yuǎn)不會(huì)為0,導(dǎo)致指針永遠(yuǎn)不會(huì)析構(gòu),產(chǎn)生了內(nèi)存泄漏,如何解決這種問(wèn)題呢,答案是使用weak_ptr。
weak_ptr
weak_ptr是用來(lái)監(jiān)視shared_ptr的生命周期,它不管理shared_ptr內(nèi)部的指針,它的拷貝的析構(gòu)都不會(huì)影響引用計(jì)數(shù),純粹是作為一個(gè)旁觀者監(jiān)視shared_ptr中管理的資源是否存在,可以用來(lái)返回this指針和解決循環(huán)引用問(wèn)題。
作用1:返回this指針,上面介紹的shared_from_this()其實(shí)就是通過(guò)weak_ptr返回的this指針,這里參考我之前寫的源碼分析shared_ptr實(shí)現(xiàn)的文章,最后附上鏈接。
作用2:解決循環(huán)引用問(wèn)題。
struct A;
struct B;
struct A {
std::shared_ptr<B> bptr;
~A() {
cout << "A delete" << endl;
}
void Print() {
cout << "A" << endl;
}
};
struct B {
std::weak_ptr<A> aptr; // 這里改成weak_ptr
~B() {
cout << "B delete" << endl;
}
void PrintA() {
if (!aptr.expired()) { // 監(jiān)視shared_ptr的生命周期
auto ptr = aptr.lock();
ptr->Print();
}
}
};
int main() {
auto aaptr = std::make_shared<A>();
auto bbptr = std::make_shared<B>();
aaptr->bptr = bbptr;
bbptr->aptr = aaptr;
bbptr->PrintA();
return 0;
}
輸出:
A
A delete
B delete
unique_ptr
std::unique_ptr是一個(gè)獨(dú)占型的智能指針,它不允許其它智能指針共享其內(nèi)部指針,也不允許unique_ptr的拷貝和賦值。使用方法和shared_ptr類似,區(qū)別是不可以拷貝:
using namespace std;
struct A {
~A() {
cout << "A delete" << endl;
}
void Print() {
cout << "A" << endl;
}
};
int main() {
auto ptr = std::unique_ptr<A>(new A);
auto tptr = std::make_unique<A>(); // error, c++11還不行,需要c++14
std::unique_ptr<A> tem = ptr; // error, unique_ptr不允許移動(dòng)
ptr->Print();
return 0;
}
unique_ptr也可以像shared_ptr一樣自定義刪除器,使用方法和shared_ptr相同。
關(guān)于c++11的智能指針的使用就介紹到這里,大家有問(wèn)題可以點(diǎn)此留言 ,我會(huì)盡快回復(fù)~
參考資料
https://www.jianshu.com/p/b6ac02d406a0
https://juejin.im/post/5dcaa857e51d457f7675360b#heading-16
《深入應(yīng)用c++11:代碼優(yōu)化與工程級(jí)應(yīng)用》
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!