一 什么是智能指針
c++的智能指針是利用了c++的RAII機(jī)制冯凹,這樣可以及時(shí)的釋放資源谎亩,且即使代碼中觸發(fā)了異常,也不會(huì)因?yàn)橥泴慸elete而沒有釋放內(nèi)存宇姚。智能指針提供了一種同一資源管理的方式匈庭,就像是資源的proxy,智能指針內(nèi)部維護(hù)了一個(gè)對(duì)資源的引用計(jì)數(shù)浑劳,每當(dāng)被拷貝時(shí)就加一阱持,被釋放時(shí)減一。
二 正確的智能指針實(shí)例
class B;
class A {
public:
A() {
cout << "構(gòu)造 A" << endl;
}
~A() {
cout << "析構(gòu) A" << endl;
}
};
class B {
public:
B() {
cout << "構(gòu)造 B" << endl;
}
~B() {
cout << "析構(gòu) B" << endl;
}
};
void test() {
shared_ptr<A> ap(new A());
shared_ptr<B> bp(new B());
cout << ap.use_count() << endl;
cout << bp.use_count() << endl;
}
int main() {
test();
return 0;
}
結(jié)果:ap和bp的引用計(jì)數(shù)都為1魔熏。
三 異常情況:循環(huán)依賴
class B;
class A {
public:
shared_ptr<B> spb;
A() {
cout << "構(gòu)造 A" << endl;
}
~A() {
cout << "析構(gòu) A" << endl;
}
};
class B {
public:
shared_ptr<A> spa;
B() {
cout << "構(gòu)造 B" << endl;
}
~B() {
cout << "析構(gòu) B" << endl;
}
};
void test() {
shared_ptr<A> ap(new A());
shared_ptr<B> bp(new B());
cout << ap.use_count() << endl;
cout << bp.use_count() << endl;
if (true) {
ap -> spb = bp;
bp -> spa = ap;
}
cout << ap.use_count() << endl;
cout << bp.use_count() << endl;
}
int main() {
test();
return 0;
}
輸出:
構(gòu)造 A
構(gòu)造 B
1
1
2
2
ap和bp兩個(gè)智能指針中出現(xiàn)了循環(huán)依賴的情況衷咽,導(dǎo)致本應(yīng)該在if(true)結(jié)束后就釋放的資源,無法進(jìn)行釋放蒜绽,引用計(jì)數(shù)本應(yīng)降為1镶骗,實(shí)際還為2。
那么如何解決這種循環(huán)依賴問題呢躲雅,c++標(biāo)準(zhǔn)庫提供了另外一種智能指針weak_ptr鼎姊,weak_ptr 只能由shared_ptr或者其它的weak_ptr構(gòu)造,weak_ptr和shared_ptr共享一個(gè)引用計(jì)數(shù)對(duì)象相赁,shared_ptr被weak_ptr拷貝時(shí)相寇,會(huì)在其引用計(jì)數(shù)對(duì)象上增加一個(gè)weak_count, 但不增加ref_count。ref_count減為0時(shí)銷毀管理資源噪生。
三 正確做法
class B;
class A {
public:
weak_ptr<B> spb;
A() {
cout << "構(gòu)造 A" << endl;
}
~A() {
cout << "析構(gòu) A" << endl;
}
};
class B {
public:
weak_ptr<A> spa;
B() {
cout << "構(gòu)造 B" << endl;
}
~B() {
cout << "析構(gòu) B" << endl;
}
};
void test() {
shared_ptr<A> ap(new A());
shared_ptr<B> bp(new B());
cout << ap.use_count() << endl;
cout << bp.use_count() << endl;
if (true) {
ap -> spb = bp;
bp -> spa = ap;
}
cout << ap.use_count() << endl;
cout << bp.use_count() << endl;
}
int main() {
test();
return 0;
}
輸出:
構(gòu)造 A
構(gòu)造 B
1
1
1
1
析構(gòu) B
析構(gòu) A
引用計(jì)數(shù)減了裆赵。
智能指針的線程安全問題
根據(jù)c++的標(biāo)準(zhǔn),shared_ptr提供了int和std::string等一個(gè)級(jí)別的線程安全級(jí)別跺嗽。也就是說不能保證其線程安全战授,這里引用stackOverFlow上面的一個(gè)回答:
std::shared_ptr
is not thread safe.
A shared pointer is a pair of two pointers, one to the object and one to a control block (holding the ref counter, links to weak pointers ...).
There can be multiple std::shared_ptr and whenever they access the control block to change the reference counter it's thread-safe but thestd::shared_ptr
itself is NOT thread-safe or atomic.
If you assign a new object to astd::shared_ptr
while another thread uses it, it might end up with the new object pointer but still using a pointer to the control block of the old object => CRASH.
大意為一個(gè)shared_ptr有兩個(gè)重要成員變量,ref_count和shared_ptr桨嫁,ref_count本身是原子的植兰,可以保證其線程安全,但是shared_ptr指針并不是線程安全的璃吧。所以如果將shared_ptr reset為一個(gè)新的智能指針楣导,那么可能會(huì)出現(xiàn)新指針指向舊的控制塊,導(dǎo)致崩潰畜挨。
還有智能指針指向元素的線程安全性由對(duì)象自己控制筒繁。