auto_ptr和shared_ptr
http://patmusing.blog.163.com/blog/static/13583496020101824541270/
a. auto_ptr定義于頭文件memory中;
?
b. auto_ptr只能用來管理單個動態創建的對象,而不能管理動態創建的數組;
?
c.?和其他copy和assign不同,auto_ptr的copy和assign會改變右邊的操作數,assignment符號的兩邊的auto_ptr均為左值;There is a crucially important difference between how auto_ptr and built-in pointers treat copy and assignment. When we
copy an auto_ptr or assign its value to another auto_ptr, ownership of the underlying object is transferred from the original
to the copy. The original auto_ptr is reset to an unbound state;
?
d. auto_ptr不能作為容器中的元素;
??auto_ptr的copy和assign具有析構行為,這就是auto_ptr不能作為容器元素的原因,因為標準庫中的容器有對元素的要求:經
過copy或者assign后的兩個對象,必須相等;
?
e.?在判斷一個auto_ptr是否被綁定的時候,不能直接使用auto_ptr對象:
??????auto_ptr<Student> stu1(new?Student);
?????????if(stu1)
?????????{
???????????????????cout <<?"stu1 is bound"?<< endl;
?????????}
?????????else
?????????{
???????????????????cout <<?"stu1 is unbound"?<< endl;
?????????}
?????????這樣做將會導致compile error,應該改為:
?????????auto_ptr<Student> stu1(new?Student);
?????????if(stu1.get())???????????????// get()獲取的是underlying對象的指針,如果被綁定則非零,如果沒有被綁定則為0
?????????{
???????????????????cout <<?"stu1 is bound"?<< endl;
?????????}
?????????else
?????????{
???????????????????cout <<?"stu1 is unbound"?<< endl;
?????????}
?
f. auto_ptr的構造函數是explicit的,消除了隱式的類型轉換(在這里即,從指針類型到auto_ptr類型的轉換),因此不能直接將一個
指針賦給一個auto_ptr對象。如下面這樣的代碼:
?????????auto_ptr<Student> stu5 =?new?Student;
?????????stu5->printStudentInfo();
??在編譯的時候不會有問題,但會出現嚴重的runtime error。正確的做法應該是:
?????????auto_ptr<Student> stu5(new?Student);
?????????stu5->printStudentInfo();
?
g.?不同用兩個auto_ptr綁定到同一個對象。
??????// stu6和stu7綁定到了同一個對象,這將會導致該對象被析構兩次,將會產生runtime error
?????????auto_ptr<Student> stu6(new?Student("Evanligine",?"F", 8));
?????????auto_ptr<Student> stu7(stu6.get());
?????????后面一句,應該改為:
?????????auto_ptr<Student> stu7(stu6);
?????????這樣stu6就將ownership轉交給了stu7,stu6則成為了unbound的auto_ptr。????????????
?
h.?不能用auto_ptr指向靜態資源分配對象。如下面的代碼,盡管可以通過編譯,但將會產生runtime error:
?????????int?ix = 10;
?????????auto_ptr<int> pint1(&ix);
?
i. auto_ptr的重要操作
auto_ptr<T> ap;???????????? ??創建一個未綁定的auto_ptr對象ap
auto_ptr<T> ap(p);??????? ???創建一個auto_ptr對象ap,它綁定了指針p所指向的對象。該構造函數是explicit的
auto_ptr<T> ap1(ap2);??????創建一個auto_ptr對象ap1,它綁定到原來被ap2綁定的對象,ap2則成為未綁定的auto_ptr
ap1 = ap2;???????????????????????ap1刪除原來綁定的對象,ap2將ownership移交給ap1,ap2成為未綁定的auto_ptr
*ap??????????????????????????????????返回ap綁定的對象的引用。可以通過*給被綁定的內在對象賦值。如下面代碼:
????????????????????????????????????????????????????????auto_ptr<int> pint(new?int(3));
????????????????????????????????????????????????????????cout << *pint << endl;???????????????????????????//?輸出3
????????????????????????????????????????????????????????*pint = 100;
????????????????????????????????????????????????????????cout << *pint << endl;???????????????????????????//?輸出100
ap->????????????????????????????????返回被ap綁定的對象的指針
ap.reset(p)???????????????????????如果指針p和ap綁定的內存對象的指針不相同,那么ap刪除被其綁定的內存對象,改而綁定p所
???????? 指向的對象.
ap.release() ? ? ? ? ? ? ? ? ? ?ap內部指向為NULL,但是原來所指內存并不會主動釋放,要手動去釋放調用delete
ap.get()???????????????????????????返回ap所綁定對象的指針
#include <iostream> #include <memory>using namespace std;class A {public:int a;A(int aa):a(aa) {cout << "constructing A = " << a<<endl;}~A() {cout << "deconstruct A = " << a <<endl;}void fun() {cout << "print A = " << a <<endl;} };int main() {{auto_ptr<A> p1(new A(1));if(p1.get()) {p1.get()->a = 2;(*p1.get()).a = 3;p1.get()->fun();(*p1).a = 4;//*p1就是得到了原對象的引用(*p1).fun();p1->a = 5;//p1->返回的是原對象的指針p1->fun();auto_ptr<A> p2 = p1;cout << p1.get()<<endl;p2->fun();//p2.release();//比較危險,只是p2指向NULL,但是原來的內存不釋放,即使出了這個作用域//cout << "releas p2\n";//cout << p2.get()<<endl;p2.reset(new A(12));p2.reset();//主動釋放內存cout << "bounded\n";}else{cout << "unbound\n";}}cout << "out of area\n";return 0; }
1、auto_ptr不能共享所有權。
2、auto_ptr不能指向數組
3、auto_ptr不能作為容器的成員。
4、不能通過賦值操作來初始化auto_ptr
std::auto_ptr<int>?p(new?int(42));?????//OK
std::auto_ptr<int>?p?=?new?int(42);????//ERROR
這是因為auto_ptr?的構造函數被定義為了explicit
5、不要把auto_ptr放入容器
對于shared_ptr先看個例子
如果是p1->m_other = p2;
先要釋放p2的資源,但是p2被引用了兩次,所以釋放只是把引用次數變為1
在釋放p1的資源,先進入析構函數,釋放p1,在析構p1的內部成員變量,就是p2,這時p2發現自己的引用次數為1了,再調用p2的析構
如果是p2->m_other = p1
先進入p2的析構函數,再去析構p2的成員變量m_other,發現其引用次數為2,所以只是講其引用次數變為1
在進入p1的析構函數
循環引用
class B;class A {public:int a;A(int aa):a(aa) {cout << "constructing A = " << a<<endl;}~A() {cout << "deconstruct A = " << a <<endl;}void fun() {cout << "print A = " << a <<endl;}tr1::shared_ptr<B> m_other; };class B {public:int b;B(int bb):b(bb) {cout << "constructing B = " << b<<endl;}~B() {cout << "deconstruct B = " << b <<endl;}void fun() {cout << "print B = " << b <<endl;}tr1::shared_ptr<A> m_other; };int main() {tr1::weak_ptr<A> wp;{tr1::shared_ptr<A> pa(new A(1));tr1::shared_ptr<B> pb(new B(2));pb->m_other = pa;pa->m_other = pb;} }output:
constructing A = 1
constructing B = 2
bounded
out of area
都沒進入析構函數釋放資源
先釋放pb,發現pb指向的元素被引用兩次,所以只是將引用次數變為1
后釋放pa,同理,將引用次數變為1
這里就有了內存泄露
需要用weak_ptr打破循環引用
把class A 或者B中任何一個或者兩個m_other變為weak_ptr就可以解決上述問題
weak_ptr
weak_ptr本身不具有指針的行為,例如你不能對一個weak_ptr來進行*或者->操作。它通常用來和shared_ptr配合使用。
weak_ptr作為一個”shared_ptr的觀察者”能夠獲知shared_ptr的引用計數,還可以獲知一個shared_ptr是否已經被析構了。單沖這一點來說,就一點不weak了
構造weak_ptr
有兩種方法可以構造一個weak_ptr
1、 從shared_ptr構造而來。這種情況不會增加shared_ptr的引用計數。當然會增加另一個計數,這個放到下一篇中講。
2、 從另一個weak_ptr拷貝。
也就是說weak_ptr不可能脫離shared_ptr而存在。
expired()
返回布爾,當返回true的時候表示,weak_ptr關聯的shared_ptr已經被析構了。
int _tmain(int argc, _TCHAR* argv[])
{
shared_ptr<foo> fptr=shared_ptr<foo>(new foo(1,2));
weak_ptr<foo> wptr=fptr;
fptr.reset();
if(wptr.expired())
{
cout<<”wptr has expired”<<endl;
}
system(“pause”);
return 0;
}
lock()
從當前的weak_ptr創建一個新的shared_ptr。如果此時expired()返回true時,創建的shared_ptr中將保存一個null_ptr。
use_count()
返回當前關聯的shared_ptr的引用計數是多少。expired()返回true時,該函數返回0。
weak_ptr使用場景
weak_ptr的特性是:weak_ptr不會增加shared_ptr的引用計數,所以weak_ptr通常用來解決shared_ptr無法解決的問題,例如環形引用。weak_ptr常見的使用場景有這么幾個:
1、 想管理某些資源,但是又不想增加引用計數,那么就可以保存weak_ptr。
2、 當知道了有環形引用后,可以使用weak_ptr。例如上面的例子可以改為這樣:
class CParent
{
public:
shared_ptr< CChild > children;
};
class CChild
{
public:
weak_ptr< CParent > parent;
};
int main()
{
{
shared_ptr< CParent > pA(new CParent);
shared_ptr< CChild > pB(new CChild);
pA-> children =pB;
pB-> parent =pA;
}
}
3、 某些情況下,需要知道某個shared_ptr是否已經釋放了。
總結
1、 在遺留代碼上如果要引入shared_ptr要謹慎!shared_ptr帶來的不確定性可能要比帶來的便利性大的多。
2、 使用shared_ptr并不是意味著能偷懶。反而你更需要了解用shared_ptr管理的對象的生命周期應該是什么樣子的,是不是有環形引用,是不是有線程安全問題,是不是會在某個地方意外的被某個東西hold住了。
3、 一個對象如何使用shared_ptr管理那么最好全部使用shared_ptr來管理,必要的時候可以使用weak_ptr。千萬不要raw ptr和智能指針混用
4、 多線程讀寫同一個shared_ptr的時候,可以先加鎖拷貝一份出來,然后解鎖即可。
http://www.sssa2000.com/?p=915
總結
以上是生活随笔為你收集整理的auto_ptr和shared_ptr的全部內容,希望文章能夠幫你解決所遇到的問題。