記憶體管理與智能指針
作為一名合格的C++程式猿,管理記憶體是不可逃避的現實。像java,python等這些面向對象的語言來說,它們有垃圾回收機制,不需要程式員來管理記憶體。然而為了追求效率的C++沒有實作這一機制。那麼我們就必須借助各種技術來解決記憶體洩露這一頭疼的事情了。
C++中記憶體洩露是防不勝防的,因為畢竟人的能力是有限的。那麼我們就必須借助一些技術來減少出錯的可能。能存管理說的主要是管理由malloc,new配置設定的堆空間。而操作這些空間的句柄就是指針。歸根結底就是對指針的操作。以避免造成野指針,懸垂指針以及記憶體洩露的情況,看了一些智能指針的例子以及STL後,一直想要動手自己寫一套記憶體管理的工具。
一、 基礎準備
1.記憶體配置設定方式
要想實作記憶體管理,首先要了解配置設定的方式:(1)變量配置設定,(2)常量的配置設定。
1. 變量的配置設定(class T)
T * pt = new T()
T* pt = new T(const T&t)
這兩種方式分别用到了預設構造函數,以及拷貝構造函數,對于帶形參的構造函數的調用有待使用變長參數清單實作。
2. 常量的配置設定(class)
const T* pt = new const T()
const T* pt = new const T(const T& t)
2.記憶體配置設定表
通過維護一個記憶體配置設定表來實作對已配置設定記憶體的動态跟蹤,對于記憶體配置設定表也有兩種實作方案:(1)全局配置設定表;(2)類型配置設定表。
1. 全局配置設定表
指的是整個程式共有同一份配置設定表,那麼配置設定表。這裡會有一個技術問題,配置設定的記憶體類型并不統一,那麼存儲形式隻能是如下兩種情況:
a. map<void*,int*>
b. map<Object*,int*>
對于a我們無法通過配置設定表來實作對記憶體的釋放,C++無法delete空指針。那麼我們隻能在智能指針的對象中對其進行釋放(因為它了解自己的類型)。但是這有一個問題,當我們配置設定了一段記憶體,但沒有任何智能指針指向它時,它将無法釋放。有一個解決方案,那就是在構造時内部預設生成一個智能指針指向它,這回帶來不必要的記憶體開銷。或者強制要求使用者必須用至少一個職能指針指向配置設定器配置設定的記憶體。不僅如此,在性能上整個程式共有一個配置設定表會造成配置設定表size異常的龐大,影響查詢速度,同時影響多線程程式的速度。是以此方案不可取。
對于Object是一個公有子類,任何其他類必須繼承它,它有一個虛析構函數,這樣可以實作統一銷毀對象。然而C++中沒有這個公共子類,那麼适用範圍為:(1)自定義類型;(2)包裝已有類型。
class Object{
public:
Object(){}
virtaul ~Object() = 0;
}
template<classT>
class Dynamic:public Object{
public:
Dynamic();
Dynamic(const T&);
Dynamic(const Dynamic&);
T& self();
private:
T data;
};
這樣包裝之後可以讓所有類型從某種意義上來說繼承自Object.對于處理自定義類型那就更加容易了,隻需要一個公共基類,當然使用(2)同樣可以處理自定義類型。
2. 類型配置設定表
指的是一個類型一個配置設定表,如int,string等每個類型一個配置設定表。這樣的話我們智能指針可以跟蹤記憶體的釋放,同時當出現遺漏時,如沒有任何智能指針指向該段記憶體,配置設定器可以負責釋放(因為此時配置設定器知道自己配置設定的具體類型)。同時這樣配置設定表也會相對較短,查找效率更高。此時的配置設定表為:
map<T*,int*>
3. python思想遷移
我們可以想想,其實所有配置設定在棧和靜态存儲區的存儲空間都是由一個變量來辨別,這是由系統自己管理的記憶體空間,而堆上配置設定的空間在每次運作期都是不定的,也就是不是編譯期決定的,是以隻能用指針來辨別。那麼我們是否可以将這種指針辨別的記憶體空間标量化,對象化呢?其實是可以的,事實上python就是這麼做的。通過采用引用計數的方式來管理這段記憶體。我們實作在此:C++實作Python變量。
二、 需求分析
除了上面的python思想實作了之外,我們還需要實作類型配置設定表這種方法。我們需要的功能有:
1. 每個類型有個統一的記憶體配置設定器alloctor負責配置設定記憶體,同時alloctor維護者一個記憶體配置設定表map<T*,int*>(int*用于引用計數)。
2. 每種類型有個成員生成器Dynamic<T>::Construct()。
3. 智能指針跟蹤記憶體配置設定表的引用計數。
4. 當智能指針與空間脫離綁定(生命周期結束/指向其他空間),檢測到引用計數為0時,調用配置設定器的銷毀函數destroy(T*)對其 進行銷毀。當程式結束時配置設定器會檢查遺漏的空間,并将其一并銷毀。
5. 程式員被建議不要顯示使用malloc/free,new/delete關鍵字,如果使用了,那麼這部分資源的釋放你必須負責。
6. 這種智能指針的好處在于,它可以指向靜态資料和棧資料。而依據python思想設計的Obj_ptr永遠都不會和這兩類資料邦定。兩者的差異還是較為明顯的。基于類型配置設定表方案的設計,智能指針仍然具有指針語義,它可以是NULL,而基于python的設計思想Obj_ptr已經具有值語義。
三、 實作
1.Alloctor(配置設定器)
template<class T>
class Alloctor{
public:
typedef typename map<T*,int*>::value_type value_type;
typedef typename map<T*,int*>::iterator iterator;
public:
Alloctor(){}
//template<class T>
T* Construct()
{
T *get = new T();
T *p = get;
int *useCount = new int(0);
obj.insert(value_type(p,useCount));
return get;
}
//template<class Ty>
T* Construct(const T &t)
{
T *get = new T(t);
T *p = get;
int *useCount = new int(0);
obj.insert(value_type(p,useCount));
return get;
}
~Alloctor();
int* getUseCount(T* oj);
bool getElement(iterator iter,T* oj);
void destroy(T* oj);
int ListLen(){return obj.size();}
private:
map<T*,int*> obj;
};
template<class T>
Alloctor<T>::~Alloctor()
{
std::cout<<"Alloctor<T>::~Alloctor()"<<std::endl;
typename map<T*,int*>::iterator iter = obj.begin();
typename map<T*,int*>::iterator end = obj.end();
for(;iter != end;iter++)
{
delete iter->first;
delete iter->second;
}
}
template<class T>
int* Alloctor<T>::getUseCount(T* oj)
{
iterator iter = obj.find(oj);
if(iter == obj.end())return NULL;
else{
return iter->second;
}
}
template<class T>
bool Alloctor<T>::getElement(iterator iter,T* oj)
{
iter = obj.find(oj);
if(iter == obj.end())return false;
else{
return true;
}
}
template<class T>
void Alloctor<T>::destroy(T* oj) //public is not very security.
{
std::cout<<"Alloctor<T>::destroy(T* oj)"<<std::endl;
int *count = getUseCount(oj);
if(count != NULL)
{
(*count)--;
if(*count == 0)
{
obj.erase(oj);
delete oj;
}
}
}
2.成員生成器Dynamic<T>::Construct()
template<class T,class alloc = Alloctor<T> >
class Dynamic{
public:
static T* Construct()
{
return alloctor.Construct();
}
static T* Construct(const T &t)
{
return alloctor.Construct(t);
}
//private:
static alloc alloctor;
};
template<class T,class alloc> alloc Dynamic<T,alloc>::alloctor = Alloctor<T>();
3.智能指針
template<class T>
class Obj_ptr{
public:
typedef typename map<T*,int*>::value_type value_type;
typedef typename map<T*,int*>::iterator iterator;
public:
Obj_ptr();
Obj_ptr(const Obj_ptr &ptr);
Obj_ptr(T *ptr);
Obj_ptr& operator=(const Obj_ptr &ptr);
Obj_ptr& operator=(T *ptr);
bool operator==(const T *ptr){return p == ptr;}
bool operator==(const Obj_ptr &ptr){return p == ptr.p;}
T& operator*(){return *p;}
T*& operator->(){return p;}
~Obj_ptr();
void Destory();
private:
T *p;
};
//
/*Obj_ptr*/
template<class T>
Obj_ptr<T>::Obj_ptr()
{
std::cout<<"Obj_ptr()."<<std::endl;
p = NULL;
}
template<class T>
Obj_ptr<T>::Obj_ptr(const Obj_ptr &ptr)
{
std::cout<<"const Obj_ptr(Obj_ptr &ptr)."<<std::endl;
p = ptr.p;
int *count = Dynamic<T>::alloctor.getUseCount(p);
if(count != NULL)
{
(*count)++;
}
else{
//p = NULL;
}
}
template<class T>
Obj_ptr<T>::Obj_ptr(T *ptr)
{
std::cout<<"Obj_ptr(T *ptr)."<<std::endl;
p = ptr;
int *count = Dynamic<T>::alloctor.getUseCount(p);
if(count != NULL)
{
(*count)++;
std::cout<<"count:"<<*count<<std::endl;
}
else{
//p = NULL;
}
}
template<class T>
Obj_ptr<T>& Obj_ptr<T>::operator=(const Obj_ptr &ptr)
{
std::cout<<"Obj_ptr operator=(Obj_ptr &ptr)."<<std::endl;
if(&ptr != this)//not give self to self.
{
int *count = Dynamic<T>::alloctor.getUseCount(p);
if(count != NULL)
{
if(*count == 1)
{
Destory();
}
else{
(*count)--;
}
std::cout<<"count--:"<<*count<<std::endl;
}
p = ptr.p;
count = Dynamic<T>::alloctor.getUseCount(p);
if(count != NULL)
{
(*count)++;
std::cout<<"count++:"<<*count<<std::endl;
}
else{
//p = NULL;
}
}
return *this;
}
template<class T>
Obj_ptr<T>& Obj_ptr<T>::operator=(T *ptr)
{
std::cout<<"Obj_ptr operator=(T *ptr)."<<std::endl;
int *count = Dynamic<T>::alloctor.getUseCount(p);
if(count != NULL)
{
if(*count == 1)
{
Destory();
}
else{
(*count)--;
}
std::cout<<"count--:"<<*count<<std::endl;
}
p = ptr;
count = Dynamic<T>::alloctor.getUseCount(p);
if(count != NULL)
{
(*count)++;
std::cout<<"count++:"<<*count<<std::endl;
}
else{
//p = NULL;
}
return *this;
}
template<class T>
Obj_ptr<T>::~Obj_ptr()
{
std::cout<<"Obj_ptr<T>::~Obj_ptr()"<<std::endl;
Destory();
}
template<class T>
void Obj_ptr<T>::Destory()
{
Dynamic<T>::alloctor.destroy(p);
}
/*Obj_ptr*/
當然嫌構造器形式太複雜的話,我們可以用個函數将其簡化,這都不是關鍵。