智能指針

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.採用設計模式   單例模式

用單例模式設計_heapManager,類不管怎麼產生對象,都只產生一個對象。
限制用戶不能隨意構造對象,所以第一步要把構造函數寫成私有的。

//引用計數資源的管理類
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;//初始化

此時就是線程安全的。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章