一.智能指針的引入:
1. 當類中有指針成員時,一般有兩種方式來管理指針成員:一是採用值型的方式管理,每個類對象都保留一份指針指向的對象的拷貝;另一種更優雅的方式是使用智能指針,從而實現指針指向的對象的共享。
2. 智能指針(smart pointer)的一種通用實現技術是使用引用計數(reference count)。智能指針類將一個計數器與類指向的對象相關聯,引用計數跟蹤該類有多少個對象的指針指向同一對象。
二.智能指針的原理
每次創建類的新對象時,初始化指針並將引用計數置爲1;當對象作爲另一對象的副本而創建時,拷貝構造函數拷貝指針並增加與之相應的引用計數;對一個對象進行賦值時,賦值操作符減少左操作數所指對象的引用計數(如果引用計數爲減至0,則刪除對象),並增加右操作數所指對象的引用計數;調用析構函數時,析構函數減少引用計數(如果引用計數減至0,則刪除基礎對象)。
三.智能指針的演變過程:
auto_ptr 管理權轉讓(不建議使用)
scoped_ptr 防拷貝
shared_ptr 引用計數
一. 模擬實現auto_ptr
1. 實現原理
資源的轉移,AutoPtr的成員變量主要有T* _ptr, bool _owner,主要實現原理是在構造對象時賦予其空間的所有權,在析構函數中通過_owner的真假來釋放所有權,並且在拷貝或賦值後將_owner設爲false,轉移空間的所有權。但此做法的問題是:如果要釋放一個拷貝出來的對象,雖然原對象的_owner爲false,但會多次釋放同一塊空間,導致內存泄漏。s
2. 代碼
template<class T>
class AutoPtr
{
public:
AutoPtr(T* ptr)
:_ptr(ptr)
{}
AutoPtr(AutoPtr<T> &ap) //拷貝後將原有指向同一塊內存的指針置空
:_ptr(ap._ptr)
{
ap._ptr = NULL;
}
AutoPtr<T>& operator=(AutoPtr<T> &ap)
{
if (this != &ap)
{
delete _ptr;
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* GetPtr()//返回原指針_ptr
{
return _ptr;
}
T* operator->()
{
return _ptr;
}
~AutoPtr()
{
cout << "~AutoPtr()" << endl;
if (_ptr)
{
delete _ptr;
_ptr = NULL;
}
}
private:
T* _ptr;
};
3. 出現的問題
* 以下代碼會導致ap1訪問不了原來的空間
AutoPtr<int> a(p);
AutoPtr<int> ap1(p);
AutoPtr<int> ap2(ap1);
*ap1 = 10;
*ap2 = 20;
*不能用普通類型的引用去引用一個const類型的常量
const AutoPtr<int> ap1(p);
AutoPtr<int> ap2(ap1);//錯誤做法,因爲ap1被改變
*
void FunTest(AutoPtr<int> ap)
{}
int main()
{
AutoPtr<int> ap1(new int);
FunTest(ap1);//因爲傳值時會調用拷貝構造函數,故ap1被賦爲NULL;
return 0;
}
二. 模擬實現scoped_ptr
- 實現原理
因爲智能指針容易出現拷貝時釋放兩次的情況,所以ScopedPtr主要是爲了防止拷貝,防止拷貝的兩條必要條件是:①設置保護限定符(將拷貝構造函數和賦值運算符的重載設置成保護或者私有的,這樣就可以保證其他人在不知情的情況下定義拷貝構造和賦值運算符重載了);②對拷貝構造函數和賦值運算符重載只聲明不定義(如果不這麼做,編譯器就會自動合成拷貝構造和賦值運算符重載,就實現不了防拷貝的目的了)。 - 代碼
template<class T>
class ScopedPtr
{
public:
ScopedPtr(T* ptr)
:_ptr(ptr)
{}
T& operator*()
{
return *_ptr;
}
T* GetPtr()//返回原指針_ptr
{
return _ptr;
}
T* operator->()
{
return _ptr;
}
~ScopedPtr()
{
cout << "~ScopedPtr()" << endl;
if (_ptr)
{
delete _ptr;
_ptr = NULL;
}
}
protected://防拷貝:此處爲privated或protecte;拷貝構造和賦值函數只聲明不定義
ScopedPtr(const ScopedPtr<T> &sp);
ScopedPtr<T>& operator==(const ScopedPtr<T> &sp);
private:
T* _ptr;
};
3. 出現的問題
*在調用完成後ap1的_owner會被置爲false,而不能進行賦值運算
int FunTest(AutoPtr<int> ap)
{}
int main()
{
FunTest();
AutoPtr<int> ap1(new int);
FunTest(ap1);//
*ap1 = 10;
return 0;
}
*執行完成後調用了析構函數,ap1會成爲野指針
int FunTest()
{
AutoPtr<int> ap1(new int);
if(true)
{
AutoPtr<int> ap2(ap1);
}
}
三. 模擬實現shared_ptr
1. 實現原理
SharedPtr顧名思義就是共享指針,思想是引用計數。引入指針變量pCount,指向同一塊空間對其計數,當只有一個指針指向空間時再釋放空間,這樣可以解決多個指針指向同一塊空間釋放多次導致內存泄漏的問題。
2. 代碼
template<class T,class Deleter=Del<T>>
class SharedPtr
{
public:
SharedPtr(T* ptr = NULL)
:_ptr(ptr)
, _pCount(NULL)
{}
SharedPtr(const SharedPtr<T>& sp)
:_ptr(sp._ptr)
, _pCount(sp._pCount)
{
if(_ptr)
++*_pCount;
}
SharedPtr<T>& operator=(SharedPtr<T> sp)
{
if(this!= &sp)
{
if(_ptr && 0==(--*_pCount))
{
delete _ptr;
delete _pCount;
}
_ptr = sp._ptr;
_pCount = sp._pCount;
++(*_pCount);
}
return *this;
}
~SharedPtr()
{
cout << "~SharedPtr()" << endl;
if (--(*_pCount) == 0 && _ptr)
{
delete(_ptr);
delete _pCount;
}
}
T& operator*() //訪問的是原生態指針
{
return *_ptr;
}
T* GetPtr()//返回原指針_ptr
{
return _ptr;
}
T* operator->() //訪問的是對象所管理的空間
{
return _ptr;
}