今天遇到一個問題需要記錄一下,C++中的RAII機制對於內存泄漏問題有了很大的幫助。當我們在拿到一塊堆空間的時候,就應該用智能指針包裹起來,當對象生命週期結束的時候會自動釋放申請的內存。
智能指針的類型常用的有三種,shared_ptr,unique_ptr,weak_ptr。三種智能指針各有特點,簡單而言,shared_ptr有引用計數,unique_ptr一個對象只能指向一塊給定內存(不支持拷貝和賦值),weak_ptr是一種“弱”智能指針,不控制對象的生命週期,也不增加引用計數。
一般都是選擇shared_ptr,由於其引用計數功能,每當拷貝一個對象,引用計數就會增加1,同理,當一個shared_ptr對象銷燬時候,計數器就會減少1,當計數器爲0的時候,shared_ptr就會自動釋放其管理的對象。Shared_ptr簡單實現。
/*
* @Description: share_ptr的實現,注意不能用一個生指針給智能指針初始化(所以構造函數要聲明爲顯式)
*/
#ifndef _SHARE_PTR_H
#define _SHARE_PTR_H
#include <iostream>
#include <string>
template<typename T>
class My_Share_Ptr{
public:
My_Share_Ptr():a(NULL),num(new int(0)){ }
explicit My_Share_Ptr(T *ptr):a(ptr),num(new int(1)){ }
My_Share_Ptr(const My_Share_Ptr& ptr):a(ptr.a),num(ptr.num)
{
(*num)++;//引用計數+1
}
My_Share_Ptr operator =(const My_Share_Ptr& ptr);
~My_Share_Ptr(){
if(a && (*num)--){
delete a;
delete num;
}
}
public:
T& operator*();
T* operator->();
inline T* get() const {
return a;
}
inline int user_count() const{
return *num;
}
private:
T *a;//內部指針
int* num;//引用計數,注意這裏一定要指針
};
#endif
#include "share_ptr.h"
template<typename T>
My_Share_Ptr<T> My_Share_Ptr<T>::operator =(const My_Share_Ptr<T>& ptr){
if(this==&ptr){
return *this;
}
*num--;
if(*num==0){
delete num;
delete a;
}
num=ptr.num;
*num++;
a=ptr->a;
return *this;
}
template<typename T>
T& My_Share_Ptr<T>::operator *(){
if(num==0){
return (T*) 0;
}
return *a;
}
template<typename T>
T* My_Share_Ptr<T>::operator->(){
if(num==0){
return 0;
}
return a;
}
接下來進入重點,最近在用shared_ptr的時候發現循環引用的問題,這裏需要注意一下。解決的方法就是在類中,用weak_ptr替代shared_ptr。
#include <iostream>
#include <memory>
using namespace std;
class TestA;
class TestB;
class TestA{
public:
shared_ptr<TestB> ptr1;
public:
TestA():ptr1(NULL){ }
};
class TestB{
public:
shared_ptr<TestA> ptr2;
public:
TestB():ptr2(NULL){ }
};
int main(){
//以下四句會造成一個互相賦值的僵局(有點類似於死鎖,但是不是申請共享資源,而是形成一個鏈狀)
shared_ptr<TestA> ptrA(new TestA);
shared_ptr<TestB> ptrB(new TestB);
ptrA->ptr1=ptrB;
ptrB->ptr2=ptrA;
return 0;
}
無論ptrA還是ptrB,都會因爲引用計數沒有到達0,無法釋放在堆上的資源。