int main()
{
//裸指針
int *p =new int;
if(...)
return;
delete p;
return 0;
}
有時候忘記寫delete,或者寫了,但在程序執行過程中由於某種原因,提前結束了,會造成丟失內存
所以,建議使用智能指針,不使用裸指針,智能指針的資源一定會被釋放,不用自己寫delete
class Test
{
public:
Test(){cout<<"Test()"<<endl;}
~Test(){cout<<"~Test()"<<endl;}
};
template<typename T>
class CSmartPtr
{
public:
CSmartPtr(T *ptr = NULL):_ptr(ptr){cout<<"CSmartPtr()"<<endl;}
~CSmartPtr(){delete _ptr;cout<<"~CSmartPtr()"<<endl;}
private:
T *_ptr;
};
int main()
{
CSmartPtr<int> p1(new int);//智能指針都是棧上的,出了作用域自己析構
CSmartPtr<Test> p2(new Test);
return 0;
}
智能指針代替普通指針,最基本要提供*運算符和->運算符的重載函數
class Test
{
public:
Test(){cout<<"Test()"<<endl;}
~Test(){cout<<"~Test()"<<endl;}
void func()
{
cout<<"call Test::func"<<endl;
}
};
template<typename T>
class CSmartPtr
{
public:
CSmartPtr(T *ptr = NULL):_ptr(ptr){cout<<"CSmartPtr()"<<endl;}
~CSmartPtr(){delete _ptr;cout<<"~CSmartPtr()"<<endl;}
T& operator*(){return *_ptr;}
const T& operator*()const{return *_ptr;}
T* operator->(){return &*_ptr;}
const T* operator->()const{return &*_ptr;}
private:
T *_ptr;
};
int main()
{
CSmartPtr<int> p1(new int);//智能指針都是棧上的,出了作用域自己析構
CSmartPtr<Test> p2(new Test);
*p1 = 100;
p2->func();
return 0;
}
CSmartPtr<int> p3(p1);
問題:出現淺拷貝
//解決淺拷貝——拷貝構造
CSmartPtr(const CSmartPtr<T> &src)
{
_ptr = new T;
*_ptr = *src._ptr;
}
雖然問題解決了,但這樣寫不好,*p3 = 200;是給_ptr = new T;的那塊內存進行了賦值,沒有意義。
C++庫裏有一個智能指針auto_ptr,可以仿照它,加一個標誌位。
template<typename T>
class CSmartPtr
{
public:
CSmartPtr(T *ptr = NULL):_ptr(ptr),_owns(true)
{
cout<<"CSmartPtr()"<<endl;
}
~CSmartPtr()//釋放資源
{
if (_owns)
{
delete _ptr;
}
cout<<"~CSmartPtr()"<<endl;
}
CSmartPtr(const CSmartPtr<T> &src)
{
//src._owns = false;//const 修飾常對象不能修改
src.release();
_ptr = src._ptr;
cout<<"CSmartPtr(const CSmartPtr<T> &src)"<<endl;
}
T& operator*(){return *_ptr;}
const T& operator*()const{return *_ptr;}
T* operator->(){return &*_ptr;}
const T* operator->()const{return &*_ptr;}
void release()const
{
((CSmartPtr<T>*)this)->_owns = false;
}
private:
//標誌位,控制對資源的擁有者,纔可以釋放
bool _owns;
T *_ptr;
};
int main()
{
CSmartPtr<int> p1(new int);//智能指針都是棧上的,出了作用域自己析構
CSmartPtr<int> p4(new int);
*p1 = 100;
CSmartPtr<int> p3(p1);
*p3 = 200;
return 0;
}
但這種寫法只能控制一個資源,多種資源時就不能使用這種方法了。需要使用帶有引用計數的智能指針,方便管理資源的開闢和釋放,一個資源配一個引用計數。
//引用計數資源的管理類
class CHeapManager
{
public:
void addRef(void *ptr)
{
vector<ResItem>::iterator it = find(_vec.begin(),_vec.end(),ptr);
if (it == _vec.end())
{
ResItem item(ptr);//ptr構造新資源
_vec.push_back(item);
cout<<"new res addr:"<<ptr<<" ref:"<<1<<endl;
}
else
{
it->_refcount++;//給該資源引用計數加加
cout<<"add res addr:"<<ptr<<" ref:"<<it->_refcount<<endl;
}
}
void delRef(void *ptr)
{
vector<ResItem>::iterator it = find(_vec.begin(),_vec.end(),ptr);
if (it != _vec.end())
{
it->_refcount--;
cout<<"del res addr:"<<ptr<<" ref:"<<it->_refcount<<endl;
}
}
int getRef(void *ptr)
{
vector<ResItem>::iterator it = find(_vec.begin(),_vec.end(),ptr);
if (it != _vec.end())
{
return it->_refcount;
}
throw"no resource!";
}
private:
//資源結點類型
struct ResItem
{
ResItem(void *ptr = NULL):_paddr(ptr),_refcount(0)
{
if (_paddr != NULL)//新生成資源,引用計數爲1
{
_refcount = 1;
}
}
bool operator==(void *ptr)
{
return _paddr == ptr;
}
void *_paddr;//記錄資源地址
int _refcount;
};
vector<ResItem> _vec;//用來插入、刪除、查詢,存放資源
};
template<typename T>
class CSmartPtr
{
public:
CSmartPtr(T *ptr = NULL):_ptr(ptr)
{
if (_ptr != NULL)//說明新生成的智能指針引用的是一個新的資源
{
addRef();//增加該資源的引用計數
}
}
~CSmartPtr()//釋放資源
{
delRef();//先減少該資源的引用計數
if(0==getRef())//獲取該資源的引用計數,如果引用計數爲0
delete _ptr;//釋放
}
//拷貝構造
CSmartPtr(const CSmartPtr<T> &src):_ptr(src._ptr)
{
if (_ptr != NULL)//說明新生成的智能指針引用的是一個新的資源
{
addRef();//增加該資源的引用計數
}
}
//賦值運算符重載
CSmartPtr<T>& operator=(const CSmartPtr<T> &src)
{
if (this == &src)
return *this;
//不再引用之前的資源
delRef();//先減少該資源的引用計數
if(0==getRef())//獲取該資源的引用計數,如果引用計數爲0
delete _ptr;//釋放
_ptr = src._ptr;
if (_ptr != NULL)//說明新生成的智能指針引用的是一個新的資源
{
addRef();//增加該資源的引用計數
}
return *this;
}
T& operator*(){return *_ptr;}
const T& operator*()const{return *_ptr;}
T* operator->(){return &*_ptr;}
const T* operator->()const{return &*_ptr;}
void addRef(){_heapManager.addRef(_ptr);}
void delRef(){_heapManager.delRef(_ptr);}
int getRef(){return _heapManager.getRef(_ptr);}
private:
T *_ptr;
//帶有引用計數的智能指針,方便管理資源的開闢和釋放
//一個資源配一個引用計數
static CHeapManager _heapManager;//必須在類外初始化
};
template<typename T>
CHeapManager CSmartPtr<T>::_heapManager;
int main()
{
CSmartPtr<int> p1(new int);//智能指針都是棧上的,出了作用域自己析構
CSmartPtr<int> p4(new int);
*p1 = 100;
CSmartPtr<int> p3(p1);
*p3 = 200;
p3 = p4;
return 0;
}
int main()
{
int *p = new int;
CSmartPtr<int> p1(p);
CSmartPtr<char> p2((char *)p);
return 0;
}
出現問題:同一塊內存釋放了兩次,智能指針p1,p2的類型不同導致_heapManager不同,相當於有兩個管理者,兩個都認爲這個資源是自己管理的一個新資源
所謂靜態成員變量,是指同一類對象所共享。
怎麼讓不同類型實例化的智能指針擁有同一個_heapManager??
1.定義成全局的
CHeapManager _heapManager;
2.採用設計模式 單例模式
限制用戶不能隨意構造對象,所以第一步要把構造函數寫成私有的。
//引用計數資源的管理類
class CHeapManager
{
public:
//靜態的方法,返回單例模式的類對象,唯一的一個對象
static CHeapManager& getItance()
{
static CHeapManager _heapManager;
return _heapManager;
}
void addRef(void *ptr)
{
vector<ResItem>::iterator it = find(_vec.begin(),_vec.end(),ptr);
if (it == _vec.end())
{
ResItem item(ptr);//ptr構造新資源
_vec.push_back(item);
cout<<"new res addr:"<<ptr<<" ref:"<<1<<endl;
}
else
{
it->_refcount++;//給該資源引用計數加加
cout<<"add res addr:"<<ptr<<" ref:"<<it->_refcount<<endl;
}
}
void delRef(void *ptr)
{
vector<ResItem>::iterator it = find(_vec.begin(),_vec.end(),ptr);
if (it != _vec.end())
{
it->_refcount--;
cout<<"del res addr:"<<ptr<<" ref:"<<it->_refcount<<endl;
}
}
int getRef(void *ptr)
{
vector<ResItem>::iterator it = find(_vec.begin(),_vec.end(),ptr);
if (it != _vec.end())
{
return it->_refcount;
}
throw"no resource!";
}
private:
//構造函數寫成私有
CHeapManager(){}
//資源結點類型
struct ResItem
{
ResItem(void *ptr = NULL):_paddr(ptr),_refcount(0)
{
if (_paddr != NULL)//新生成資源,引用計數爲1
{
_refcount = 1;
}
}
bool operator==(void *ptr)
{
return _paddr == ptr;
}
void *_paddr;//記錄資源地址
int _refcount;
};
vector<ResItem> _vec;//用來插入、刪除、查詢,存放資源
};
template<typename T>
class CSmartPtr
{
public:
CSmartPtr(T *ptr = NULL):_ptr(ptr)
{
if (_ptr != NULL)//說明新生成的智能指針引用的是一個新的資源
{
addRef();//增加該資源的引用計數
}
}
~CSmartPtr()//釋放資源
{
delRef();//先減少該資源的引用計數
if(0==getRef())//獲取該資源的引用計數,如果引用計數爲0
delete _ptr;//釋放
}
//拷貝構造
CSmartPtr(const CSmartPtr<T> &src):_ptr(src._ptr)
{
if (_ptr != NULL)//說明新生成的智能指針引用的是一個新的資源
{
addRef();//增加該資源的引用計數
}
}
//賦值運算符重載
CSmartPtr<T>& operator=(const CSmartPtr<T> &src)
{
if (this == &src)
return *this;
//不再引用之前的資源
delRef();//先減少該資源的引用計數
if(0==getRef())//獲取該資源的引用計數,如果引用計數爲0
delete _ptr;//釋放
_ptr = src._ptr;
if (_ptr != NULL)//說明新生成的智能指針引用的是一個新的資源
{
addRef();//增加該資源的引用計數
}
return *this;
}
T& operator*(){return *_ptr;}
const T& operator*()const{return *_ptr;}
T* operator->(){return &*_ptr;}
const T* operator->()const{return &*_ptr;}
void addRef(){_heapManager.addRef(_ptr);}
void delRef(){_heapManager.delRef(_ptr);}
int getRef(){return _heapManager.getRef(_ptr);}
private:
T *_ptr;
//用單例模式設計_heapManager
static CHeapManager &_heapManager;//用&
};
template<typename T>
CHeapManager& CSmartPtr<T>::_heapManager = CHeapManager::getItance();
int main()
{
int *p = new int;
CSmartPtr<int> p1(p);
CSmartPtr<char> p2((char *)p);
return 0;
}
問題1:getItance()是不是一個可重入的函數?
是線程安全的
問題2:那這種情況呢?
int i;
//構造函數寫成私有
CHeapManager(){i++;}
不是線程安全的,static CHeapManager _heapManager;會調用構造函數,構造函數中有競態條件發生。
那怎麼寫成線程安全的?
class CHeapManager
{
public:
//靜態的方法,返回單例模式的類對象,唯一的一個對象
static CHeapManager& getItance()
{
return _heapManager;
}
void addRef(void *ptr)
{
vector<ResItem>::iterator it = find(_vec.begin(),_vec.end(),ptr);
if (it == _vec.end())
{
ResItem item(ptr);//ptr構造新資源
_vec.push_back(item);
cout<<"new res addr:"<<ptr<<" ref:"<<1<<endl;
}
else
{
it->_refcount++;//給該資源引用計數加加
cout<<"add res addr:"<<ptr<<" ref:"<<it->_refcount<<endl;
}
}
void delRef(void *ptr)
{
vector<ResItem>::iterator it = find(_vec.begin(),_vec.end(),ptr);
if (it != _vec.end())
{
it->_refcount--;
cout<<"del res addr:"<<ptr<<" ref:"<<it->_refcount<<endl;
}
}
int getRef(void *ptr)
{
vector<ResItem>::iterator it = find(_vec.begin(),_vec.end(),ptr);
if (it != _vec.end())
{
return it->_refcount;
}
throw"no resource!";
}
private:
//構造函數寫成私有
CHeapManager(){}
static CHeapManager _heapManager;
//資源結點類型
struct ResItem
{
ResItem(void *ptr = NULL):_paddr(ptr),_refcount(0)
{
if (_paddr != NULL)//新生成資源,引用計數爲1
{
_refcount = 1;
}
}
bool operator==(void *ptr)
{
return _paddr == ptr;
}
void *_paddr;//記錄資源地址
int _refcount;
};
vector<ResItem> _vec;//用來插入、刪除、查詢,存放資源
};
CHeapManager CHeapManager::_heapManager;//初始化
此時就是線程安全的。