1. 程式人生 > >一個智慧指標的實現

一個智慧指標的實現

1.什麼是智慧指標?

C++智慧指標是行為類似於指標的類物件。它使用設計模式中的代理模式,代理了原始“裸”指標的行為,為指標添加了更多更有用的特性。C++引入異常機制後,智慧指標由一種技巧升級為一種非常重要的技術,因為如果沒有智慧指標程式設計師必須保證new物件能在正確的時機delete,四處編寫異常捕獲程式碼釋放資源,而智慧指標則可以在退出作用域時—
—不管是正常離開或是因異常離開——總呼叫delete來析構在堆疊上動態分配的物件。因為C++異常處理的真正功能在於它具有為異常拋擲前構造的所有區域性物件(那麼智慧指標物件也適用)自動呼叫解構函式的能力(C++異常機制不僅僅在於它能夠處理各種不同型別的異常)。所以在異常退出智慧指標物件作用域時,總能由C++異常機制呼叫解構函式釋放在堆疊上動態分配的物件。當然,正常退出物件(智慧指標物件也屬於此列)作用域也會自動呼叫解構函式釋放在堆疊上動態分配的物件。由此可知,將“裸”指標包裝成智慧指標物件可以實現動態分配的記憶體物件的自動釋放。而C++智慧指標物件可以像原指標那樣直接使用運算子,如賦值運算子'=',指標運算子'->',解引用運算子'*'。這點從”shared_ptr智慧指標--shared_ptr模板類摘要“部分可以印證。


2.VS2008 sp1下使用智慧指標:

標頭檔案:<memory>

作用域:std::tr1::

3.自己實現智慧指標:

我們的智慧指標類是儲存在堆中分配的物件指標的類。利用物件生存期實現對記憶體的自動控制,保證記憶體能夠被正常釋放,防止記憶體洩露。我們的智慧指標物件可以賦值給另一個智慧指標物件,實現對同一塊記憶體的引用.我們在我們的類中新增一個引用計數類,這個類儲存實際指向的記憶體地址的指標和引用計數.這樣,當智慧指標物件呼叫建構函式建立時,我們將引用計數加1;當呼叫拷貝建構函式時,將引用計數加1;當呼叫=賦值時,我們將操作右值物件(賦值的智慧指標物件)引用計數加1.將左值物件(被賦值的智慧指標物件)引用計數減1(因為這個智慧指標物件不再引用原來的物件了,所以需要將計數減1,並判斷引用計數決定是否delete 引用計數指標

),同時這樣也可以防止自賦值情況,因為加1再減1頂平了~~.當呼叫解構函式時,將引用計數減1, 判斷引用計數是否為0,決定是否刪除.當然,為了智慧指標物件能夠像普通指標那樣進行*,->操作,我們還要為智慧指標物件過載*,->方法.

具體實現:

template <class T>
class Auto_Ptr;

// 智慧指標引用計數類
template <class T>
class Ref_Ptr
{
	friend class Auto_Ptr<T>;
private:
	T* m_pTtr; // 實際的指標
	size_t m_RefCount; // 引用計數
	Ref_Ptr(T* p);
	virtual ~Ref_Ptr();
};

template <class T>
Ref_Ptr<T>::Ref_Ptr(T* p)
{
	m_pTtr = p;
	m_RefCount = 1;
	printf("Ref_Ptr() 建構函式呼叫!\r\n");
}

template <class T>
Ref_Ptr<T>::~Ref_Ptr()
{
	if (m_pTtr)
	{
		printf("~Ref_Ptr() 解構函式函式呼叫");
		delete m_pTtr;
		m_RefCount =0;
	}
	m_pTtr = NULL;
}

// 智慧指標物件
template <class T>
class Auto_Ptr
{
private:
	Ref_Ptr<T>* m_pRef; // 引用計數
public:
	Auto_Ptr();
	Auto_Ptr(T* p);
	~Auto_Ptr();
	// 過載運算=,將左物件引用計數-1,並判斷是否delete;將右物件+1;
	Auto_Ptr<T> & operator = (Auto_Ptr& other);
	// 過載指標操作*,->
	T& operator *();
	T* operator ->();
	// 拷貝建構函式,引用計數+1
	Auto_Ptr(Auto_Ptr<T>& other);
};
template <class T>
Auto_Ptr<T>::Auto_Ptr()
{
	m_pRef = NULL;
}

template<class T>
Auto_Ptr<T>::Auto_Ptr(T* p)
{
	m_pRef = new Ref_Ptr<T>(p);
	printf("Auto_Ptr(T* p) 建構函式呼叫\r\n");
}

template <class T>
Auto_Ptr<T>::Auto_Ptr(Auto_Ptr<T>& other)
{
	this->m_pRef = other.m_pRef;
	++(m_pRef->m_RefCount);
	printf("Auto_Ptr(& other) 拷貝建構函式被呼叫,當前引用計數%d", this->m_pRef->m_RefCount);
}

template <class T>
Auto_Ptr<T>& Auto_Ptr<T>::operator=(Auto_Ptr& other)
{
	// 將右操作物件引用計數+1
	++(other.m_pRef->m_RefCount);

	// 由於左操作物件指向了新物件,需要將運算元-1;
	// 同時也防止了自賦值的方式.
	// 首先要判斷這個物件是否已經指向了其他物件,這個很重要!防止左指標物件為null的情況.
	if (this->m_pRef)
	{
		if (--(this->m_pRef->m_RefCount) == 0)
		{
			delete this->m_pRef;
		}
	}

	this->m_pRef = other.m_pRef;
	printf("operator = 被呼叫,當前引用計數%d", this->m_pRef->m_RefCount);
	return *this;
}

template <class T>
T& Auto_Ptr<T>::operator *()
{
	return *(m_pRef->m_pTtr);
}

template <class T>
T* Auto_Ptr<T>::operator->()
{
	return (m_pRef->m_pTtr);
}

template <class T>
Auto_Ptr<T>::~Auto_Ptr()
{
	printf("~Auto_Ptr() 解構函式被呼叫\r\n");
	if ((--m_pRef->m_RefCount) == 0)
	{
		printf("刪除\r\n");
		delete m_pRef;
		m_pRef = NULL;
	}
	if (m_pRef)
	{
			printf("當前引用計數:%d", m_pRef->m_RefCount);
	}
}

C++智慧指標物件類似於java裡的垃圾回收機制,同樣利用引用計數.

測試程式:

int _tmain(int argc, _TCHAR* argv[])
{
	Auto_Ptr<int>* pPtr = NULL;
	{
		Auto_Ptr<int> g_ptr;
		{
			// 宣告一個ptr1智慧指標,並測試*運算子
			Auto_Ptr<int> ptr1(new int(4));
			printf("%d\r\n", *ptr1);
			// 將生存期小的ptr1賦值給生存期更大的g_ptr;
			g_ptr = ptr1;
		}
		// new int(4)並沒有銷燬,因為引用計數還有1個
		Auto_Ptr<int> ptr2(new int(3));
		g_ptr = ptr2; // 此時將g_ptr指向一個新的智慧指標物件,原引用計數-1,銷燬了.(可以看到賦值左減的重要性)
		// 這個測試的是指向區域性智慧指標物件的智慧物件指標,果然是有問題的;
		// 相當於指向區域性變數地址的指標.
		Auto_Ptr<int>* pPtr = &ptr2;
		printf("%d\r\n", *(*pPtr));
	}
	Sleep(2000);
	// 錯誤,區域性物件的指標已經釋放!!!!
	//printf("%d\r\n", *(*pPtr));
	getchar();
	return 0;
}
有什麼不足錯誤之處,歡迎指正!

相關推薦

一個智慧指標實現

1.什麼是智慧指標? C++智慧指標是行為類似於指標的類物件。它使用設計模式中的代理模式,代理了原始“裸”指標的行為,為指標添加了更多更有用的特性。C++引入異常機制後,智慧指標由一種技巧升級為一種非常重要的技術,因為如果沒有智慧指標程式設計師必須保證new物件能在正確的時

智慧指標實現

原來的記憶體管理程式碼 int main(){ int *ptr = new(nothrow) int(0);  //關閉異常 if(!ptr)  { cout << "new fails." return 0; } if(!check())  ///有校驗 ,丟

簡單模仿一個智慧指標

簡單版本...代理模式和指標操作符過載的結合體... #ifndef __AUTO_PTR_H_ #define __AUTO_PTR_H_ #include <iostream> namespace std_plus { template<clas

C++智慧指標實現

上一篇介紹記憶體池的文章中提到一個基於記憶體池的智慧指標。C++沒有GC機制,需要程式設計師自己管理記憶體,而智慧指標則是C++程式設計師管理記憶體的利器。智慧指標的原理早已廣為人知,通俗來講就是用類來表示指標(用類來表示物件是OOP思想的核心),成員函式裡構建

單例的智慧指標實現

前面提出了一個問題:可不可以不需要我們手動的呼叫 release() 函式,程式結束前自動的刪除單例類的物件呢?答案是可以,使用智慧指標可以達到這個目的,這裡我們使用的是 Qt 的 QScopedPointer 來實現,也可以使用標準的 C++ 的智慧指標。 Qt 的幫助文

C++ 之 智慧指標實現

面試的時候,被問到了智慧指標,之前的確看過C++ primer,然而,都忘掉了。。。。自己實現了一下share_ptr。。寫完以後翻了翻書,才發現,和書上的並不差多少。。 智慧指標型別: share_ptr, weak_ptr, unique_ptr template &l

簡單的計數智慧指標實現

template<class T> class myCountPtr{ public: T *p_re; int *count; public: explicit myCountPtr(T *p=0)

一個簡單的智慧指標實現

#include<iostream> #include<memory> using namespace std; template<typename T> cla

C++ 智慧指標一個實現(基於模板和Shared_ptr)

自己實現了一個C++的智慧指標。 基於Shared_ptr來實現,支援預設構造,拷貝構造,移動構造, 引用計數器, 注意智慧指標中存放的指標地址一旦被一個智慧指標物件託管以後,不要再直接拿此地址來初始化其他物件,否則會引發多次洩漏的問題。(所以還是要特別小心) 如果要初始化

一個指標實現雙向連結串列

用一個指標實現雙向連結串列這個東西除了在面試中能夠用到,其他地方哪裡會用到,這個我也不知道。希望知道的人能夠在評論中說下。 下面直接給出程式碼 typedef struct _Q1LinkNode { int data; unsigned long link; }Q

C++ 自己實現智慧指標(輕量級)

文章目錄 引入 c++中間類 改進-增加引用計數 再次改進--使用模板template 再次改進--引用計數增加原子操作 引入 c++的堆和棧 class Person{ public:

C++之智慧指標和普通指標單例模式兩種實現

1  問題 實現c++的單例模式,這裡測試分別寫了通過智慧指標返回物件和普通返回指標     2  程式碼測試 include <iostream> #include <mutex> #include <m

智慧指標shared_ptr的實現

#include <iostream> using namespace std; template <class T> class smart { public: smart() = default; smart(T* ptr = nullptr):_ptr(

實現智慧指標(Smart Pointer)

#ifndef SMARTPTR_H_ #define SMARTPTR_H_ template<typename T> struct SmartPtr { explicit Smart

Solidify實現一個智慧合約17(建立BLC幣)

建立資料夾:mkdir BLC 進入後執行:truffle init 再執行:npm init,一直回車 再執行:npm install zeppelin-solidity [email protected]:/media/hisee/本地磁碟2/MyC

Solidify實現一個智慧合約9(陣列和string之間的轉換關係)

固定大小位元組陣列之間的轉換 固定大小位元組陣列,我們可以通過bytes1~32來進行宣告,固定大小位元組陣列的長度不可變,內容不可修改。 pragma solidity ^0.4.4; contr

Solidify實現一個智慧合約10(一維陣列和二維陣列)

固定長度的陣列 固定長度型別陣列的宣告及其通過length方法獲取陣列長度求和。 pragma solidity ^0.4.4; /* 陣列一旦建立,長度不可變 但裡面的內容可變 */ contract Sz { //定義長為5的陣列,並對其初始化。 uint[

Solidify實現一個智慧合約8(動態大小位元組陣列)

動態大小位元組陣列 string是一個動態尺寸的UTF-8編碼字串,它其實是一個特殊的可變位元組陣列,string是引用型別,而非值型別。 bytes動態位元組陣列,引用型別。 常規字串string轉換為bytes string字串中沒有提供length方法獲取字串長

Solidify實現一個智慧合約11(結構體)

結構體的宣告 pragma solidity ^0.4.4; contract Students { struct Person { uint age; uint stuId;

Solidify實現一個智慧合約12(字典/對映)

語法 mapping(_keyType => _ValueType) 字典/對映其實就是一個一對一鍵值對儲存關係。 舉個例子:{age:18,stuId:10003,name:"Hisee"