1. 程式人生 > >你說你會C++? —— 智能指針

你說你會C++? —— 智能指針

tro eset 釋放內存 拷貝 blog 成員 一個棧 屏蔽 lease

智能指針的設計初衷是: C++中沒有提供自己主動回收內存的機制,每次new對象之後都須要手動delete。稍不註意就memory leak。

智能指針能夠解決上面遇到的問題。

C++中常見的智能指針包含(共七種): std::auto_ptr boost::scoped_ptr boost::shared_ptr boost::intrusive_ptr boost::scoped_array boost::shared_array boost::weak_ptr

事實上,智能指針並非指針,它不過一個棧對象而已。 在棧對象的生命周期結束時,智能指針調用析構函數釋放其管理的堆內存。 全部的智能指針都重載了‘operator->‘操作符,用來返回其管理對象的引用。從而能夠 運行所管理對象的一些操作。


兩個方法: - get() 訪問智能指針包括的裸指針引用 - reset() 若傳遞參數為空或NULL 則智能指針會釋放當前管理的內存。

若傳遞參數為一個對象 則智能指針會釋放當前管理的內存,管理新傳入的對象。
假定有以下這個ManagedObj類。

class ManagedObj
{
public:
    ManagedObj(int val = 0):m_val(val)
    {
        cout<<"Obj : "<<m_val<<endl;
    }

    ~ManagedObj()
    {
        cout<<"~Obj : "<<m_val<<endl;
    }

    void testFun()
    {
        cout<<"testFun : "<<m_info<<endl;
    }

public:
    string m_info;
    int m_val;
};

-> std::auto_ptr
屬於STL 在namespace std中 加入頭文件 #include <memory>就可以使用。 auto_ptr一般用於管理單個堆內存對象。
看以下這個樣例:
// std::auto_ptr
void testAutoPtr1()
{
    auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));

    if (atPtr.get())    // 推斷智能指針是否為空
    {
        atPtr->testFun();

        atPtr.get()->m_info += " 1st append";    // get() 返回裸指針的引用
        atPtr->testFun();

        (*atPtr).m_info += " 2nd append";        // operator* 返回智能指針管理的對象
        (*atPtr).testFun();
    }
}
執行結果為: 技術分享圖片
技術分享圖片 OK 好像沒什麽問題。
我們接著測試:
void testAutoPtr2()
{
    auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));

    if (atPtr.get())    // 推斷智能指針是否為空
    {
        auto_ptr<ManagedObj> atPtr2;

        atPtr2 = atPtr;               // 原因在這行代碼
        atPtr2->testFun();
        atPtr->testFun();          // 崩潰在這行代碼
    }
}
執行結果為: 技術分享圖片
技術分享圖片 調試發現 最後一行代碼出bug了。 為什麽呢? 事實上是atPtr2剝奪了atPtr的內存管理全部權,導致atPtr懸空。
繼續測試。

void testAutoPtr3()
{
    auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));

    if (atPtr.get())    // 推斷智能指針是否為空
    {
        atPtr.release();
    }
}
執行結果為: 技術分享圖片
技術分享圖片 好像又出bug了。 我們並沒有看到析構函數被調用的跡象,內存泄漏了。。 (逗我呢 怎麽這麽多bug?

)
別著急 第三個測試函數正確的寫法應該是這種。

void testAutoPtr3()
{
    auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));

    if (atPtr.get())    // 推斷智能指針是否為空
    {
        //ManagedObj* temp = atPtr.release();
        //delete temp;

        // 或者
        atPtr.reset();
    }
}
技術分享圖片
技術分享圖片 這才是我們想要看到的結果。


所以我們也發現。auto_ptr的release()函數僅僅是讓出內存的管理權,並沒有真正的釋放內存。
總結: auto_ptr一般用於管理單個堆內存對象。

可是須要註意: a. 切記使用 "operator="。萬一真用了,就不要再使用舊的對象了。

b. release()方法不會釋放內存 只不過讓出全部權。
上面的auto_ptr使用起來確實不是非常方便,並且我們還有註意避開它的使用缺陷。

因此就出現了boost中的一些智能指針,以下一一介紹。
-> boost::scoped_ptr


屬於boost庫 在namespace boost中 加入頭文件 #include<boost/smart_ptr.hpp> 就可以使用。


關於boost庫的安裝教程 可參考: http://blog.csdn.net/misskissc/article/details/9793645 http://www.cnblogs.com/xuqiang/archive/2011/05/08/2040420.html
與auto_ptr類似。scoped_ptr也可管理單個堆內存的對象, 可是它獨享全部權,因此也就避免了auto_ptr的缺陷
OK。上代碼測試。

// boost::scoped_ptr
void testScopedPtr()
{
	boost::scoped_ptr<ManagedObj> scPtr(new ManagedObj(1, " initialize"));

	if (scPtr.get())
	{
		scPtr->testFun();

		scPtr.get()->m_info += " 1st append";	// get() 返回裸指針的引用
		scPtr->testFun();

		(*scPtr).m_info += " 2nd append";		// operator* 返回智能指針管理的對象
		(*scPtr).testFun();

		//scPtr.release();				// error scoped_ptr 沒有成員release
		
		//boost::scoped_ptr<ManagedObj> scPtr2;
		//scPtr2 = scPtr;					// error scoped_ptr<T>::operator=(const scoped_ptr<ManagedObj> &)不可訪問
	}
}
執行結果為: 技術分享圖片
技術分享圖片 註意凝視部分的代碼。 scoped_ptr沒有release()函數 屏蔽了operator=操作, 因此不會出現auto_ptr中出現的內存泄漏和崩潰問題。 但這也帶來了還有一個新問題。我們無法復制智能指針。
因此出現了接下來的shared_ptr。
-> boost::shared_ptr
屬於boost庫 在namespace boost中 加入頭文件 #include<boost/smart_ptr.hpp> 就可以使用。


因為scoped_ptr不同意賦值 拷貝 獨享內存的全部權。 這裏的shared_ptr是專門解決智能指針的內存全部權共享這個問題的。 其內部使用了引用計數。

來,測試吧。

// boost::shared_ptr
void testSharedPtr(boost::shared_ptr<ManagedObj> ptr)
{
    ptr->testFun();
    cout<<"shared_ptr use_count: "<<ptr.use_count()<<endl;
}

void testSharedPtr1()
{
    boost::shared_ptr<ManagedObj> shPtr(new ManagedObj(1, " initialize"));

    if (shPtr.get())
    {
        shPtr->testFun();

        shPtr.get()->m_info += " 1st append";    // get() 返回裸指針的引用
        shPtr->testFun();

        (*shPtr).m_info += " 2nd append";        // operator* 返回智能指針管理的對象
        (*shPtr).testFun();
    }

    cout<<"shared_ptr1 use_count: "<<shPtr.use_count()<<endl;
    testSharedPtr(shPtr);
    cout<<"shared_ptr1 use_count: "<<shPtr.use_count()<<endl;
     //shPtr.release(); // error shared_ptr 沒有成員release
}
執行結果為: 技術分享圖片
技術分享圖片 能夠看到。調用testSharedPtr()函數運行了一次復制操作,智能指針內部的引用計數+1。 在該函數調用結束後,引用計數自己主動-1。

上面介紹的幾種智能指針都針對單個對象的內存管理。

事實上智能指針也可管理數組,接下來介紹boost::scoped_array和boost::shared_array。


-> boost::scoped_array


屬於boost庫 在namespace boost中 加入頭文件 #include<boost/smart_ptr.hpp> 就可以使用。
boost::scoped_array用於管理動態數組。也是獨享全部權的。


上代碼:

// boost::scoped_array
void testScopedArray()
{
    boost::scoped_array<ManagedObj> scArr(new ManagedObj[2]);        // 動態數組初始化

    if (scArr.get())
    {
        scArr[0].testFun();

        scArr.get()[0].m_info += " [0] 1st append";
        scArr[0].testFun();

        //(*scArr)[0].m_info += " 2st append";    // error scoped_array沒有重載operator*
        //(*scArr)[0].testFun();

        //scArr[0].release();                        // error scoped_array 沒有成員release

        boost::scoped_array<ManagedObj> scArr2;
        //scArr2 = scArr;                // error operator=不可訪問 屏蔽了operator= 禁止拷貝
    }
}
執行結果為: 技術分享圖片 技術分享圖片
boost::scoped_array的使用和boost::scoped_ptr大致幾乎相同。 都禁止拷貝 獨享全部權。

註意: boost::scoped_array沒有重載operator*操作符
-> boost::shared_array


屬於boost庫 在namespace boost中 加入頭文件 #include<boost/smart_ptr.hpp> 就可以使用。


內部使用了引用計數,解決參數傳遞和拷貝賦值時的問題。


以下給出代碼測試:

// boost::shared_array
void testSharedArray(boost::shared_array<ManagedObj> pArr)
{
    cout<<"shared_arr use_count: "<<pArr.use_count()<<endl;
    boost::shared_array<ManagedObj> pTempArr;
    pTempArr = pArr;
    cout<<"shared_arr use_count: "<<pArr.use_count()<<endl;
}

void testSharedArray1()
{
    boost::shared_array<ManagedObj> shArr(new ManagedObj[2]);

    if (shArr.get())
    {
        shArr[0].testFun();

        shArr.get()[0].m_info += " [0] 1st append";
        shArr[0].testFun();
        shArr[1].testFun();

        shArr.get()[0].m_info += " [1] 1st append";
        shArr[1].testFun();
        //(*shArr)[0].m_info += " [0] 2nd append";    // error 沒有重載operator*操作符
    }

    cout<<"shared_arr1 use_count: "<<shArr.use_count()<<endl;
    testSharedArray(shArr);
    cout<<"shared_arr1 use_count: "<<shArr.use_count()<<endl;
}
執行結果為: 技術分享圖片 技術分享圖片

-> boost::weak_ptr
屬於boost庫 在namespace boost中 加入頭文件 #include<boost/smart_ptr.hpp> 就可以使用。
若我們只關心是否能使用對象。而不關心內部的引用計數。 boost::weak_ptr 是 boost::shared_ptr 的觀察者(Observer)對象, 意味著boost::weak_ptr僅僅對boost::shared_ptr引用。可是並不更新 其引用計數。被觀察的shared_ptr失效後,weak_ptr對應也失效。


上測試代碼:

// boost::weak_ptr
void testWeakPtr()
{
    boost::weak_ptr<ManagedObj> wPtr;
    boost::shared_ptr<ManagedObj> shPtr(new ManagedObj(1, "initialize"));

    cout << "testWeakPtr boost::shared_ptr UseCount: " << shPtr.use_count() << endl;
    wPtr = shPtr;
    cout << "testWeakPtr boost::shared_ptr UseCount: " << shPtr.use_count() << endl;
}
執行結果為: 技術分享圖片
技術分享圖片 我們發現賦值操作並沒有更改引用計數。 boost::weak_ptr很多其它用於以下這樣的場合: 在基類中定義一個weak_ptr,用於觀察其子類中的shared_ptr。這樣基類僅僅要看自己的weak_ptr 是否為空就知道子類有沒對自己賦值,而不影響子類中shared_ptr的引用計數。從而減少復雜度。更好管理對象。
-> boost::intrusive_ptr
屬於boost庫 在namespace boost中 加入頭文件 #include<boost/smart_ptr.hpp> 就可以使用。
intrusive_ptr是一種插入式智能指針,其內部沒有引用計數, 須要自己增加引用計數,否則會編譯錯誤。

技術分享圖片 技術分享圖片


OK 到這兒為止,七種智能指針基本上都大致介紹完了。


參考:

http://blog.csdn.net/xt_xiaotian/article/details/5714477


完整的代碼在這裏

#include <iostream>
#include <memory>
#include <string>
#include <boost/smart_ptr.hpp>			// add header file

// namespace 
using namespace std;
using namespace boost;

// heap obj class
class ManagedObj
{
public:
	ManagedObj(int val = 0, string info = "")
		:m_val(val), m_info(info)
	{
		cout<<"Obj : "<<m_val<<m_info<<endl;
	}

	~ManagedObj()
	{
		cout<<"~Obj : "<<m_val<<m_info<<endl;
	}

	void testFun()
	{
		cout<<"testFun : "<<m_info<<endl;
	}

public:
	string m_info;
	int m_val;
};

// std::auto_ptr
void testAutoPtr1()
{
	auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));
	
	if (atPtr.get())	// 推斷智能指針是否為空
	{
		atPtr->testFun();

		atPtr.get()->m_info += " 1st append";	// get() 返回裸指針的引用
		atPtr->testFun();

		(*atPtr).m_info += " 2nd append";		// operator* 返回智能指針管理的對象
		(*atPtr).testFun();
	}
}

void testAutoPtr2()
{
	auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));

	if (atPtr.get())	// 推斷智能指針是否為空
	{
		auto_ptr<ManagedObj> atPtr2;

		atPtr2 = atPtr;
		atPtr2->testFun();
		atPtr->testFun();
	}
}

void testAutoPtr3()
{
	auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize"));

	if (atPtr.get())	// 推斷智能指針是否為空
	{
		//ManagedObj* temp = atPtr.release();
		//delete temp;
		
		// 或者
		atPtr.reset();
	}
}

// boost::scoped_ptr
void testScopedPtr()
{
	boost::scoped_ptr<ManagedObj> scPtr(new ManagedObj(1, " initialize"));

	if (scPtr.get())
	{
		scPtr->testFun();

		scPtr.get()->m_info += " 1st append";	// get() 返回裸指針的引用
		scPtr->testFun();

		(*scPtr).m_info += " 2nd append";		// operator* 返回智能指針管理的對象
		(*scPtr).testFun();

		//scPtr.release();				// error scoped_ptr 沒有成員release
		
		//boost::scoped_ptr<ManagedObj> scPtr2;
		//scPtr2 = scPtr;					// error scoped_ptr<T>::operator=(const scoped_ptr<ManagedObj> &)不可訪問
	}
}

// boost::shared_ptr
void testSharedPtr(boost::shared_ptr<ManagedObj> ptr)
{
	ptr->testFun();
	cout<<"shared_ptr use_count: "<<ptr.use_count()<<endl;
}

void testSharedPtr1()
{
	boost::shared_ptr<ManagedObj> shPtr(new ManagedObj(1, " initialize"));

	if (shPtr.get())
	{
		shPtr->testFun();

		shPtr.get()->m_info += " 1st append";	// get() 返回裸指針的引用
		shPtr->testFun();

		(*shPtr).m_info += " 2nd append";		// operator* 返回智能指針管理的對象
		(*shPtr).testFun();
	}

	cout<<"shared_ptr1 use_count: "<<shPtr.use_count()<<endl;
	testSharedPtr(shPtr);
	cout<<"shared_ptr1 use_count: "<<shPtr.use_count()<<endl;

	//shPtr.release();	// error shared_ptr 沒有成員release
}

// boost::scoped_array
void testScopedArray()
{
	boost::scoped_array<ManagedObj> scArr(new ManagedObj[2]);		// 動態數組初始化

	if (scArr.get())
	{
		scArr[0].testFun();

		scArr.get()[0].m_info += " [0] 1st append";
		scArr[0].testFun();

		//(*scArr)[0].m_info += " 2st append";	// error scoped_array沒有重載operator*
		//(*scArr)[0].testFun();
		
		//scArr[0].release();						// error scoped_array 沒有成員release

		boost::scoped_array<ManagedObj> scArr2;
		//scArr2 = scArr;				// error operator=不可訪問 屏蔽了operator= 禁止拷貝
	}
}

// boost::shared_array
void testSharedArray(boost::shared_array<ManagedObj> pArr)
{
	cout<<"shared_arr use_count: "<<pArr.use_count()<<endl;

	boost::shared_array<ManagedObj> pTempArr;
	pTempArr = pArr;
	cout<<"shared_arr use_count: "<<pArr.use_count()<<endl;
}

void testSharedArray1()
{
	boost::shared_array<ManagedObj> shArr(new ManagedObj[2]);

	if (shArr.get())
	{
		shArr[0].testFun();

		shArr.get()[0].m_info += " [0] 1st append";
		shArr[0].testFun();
		shArr[1].testFun();

		shArr.get()[1].m_info += " [1] 1st append";
		shArr[1].testFun();
		//(*shArr)[0].m_info += " [0] 2nd append";	// error 沒有重載operator*操作符
	}

	cout<<"shared_arr1 use_count: "<<shArr.use_count()<<endl;
	testSharedArray(shArr);
	cout<<"shared_arr1 use_count: "<<shArr.use_count()<<endl;
}

// boost::weak_ptr
void testWeakPtr()
{
	boost::weak_ptr<ManagedObj> wPtr;
	boost::shared_ptr<ManagedObj> shPtr(new ManagedObj(1, "initialize"));

	cout << "testWeakPtr boost::shared_ptr UseCount: " << shPtr.use_count() << endl;
	wPtr = shPtr;
	cout << "testWeakPtr boost::shared_ptr UseCount: " << shPtr.use_count() << endl;
}

// boost::intrusive_ptr
void testIntrusivePtr()
{
	//boost::intrusive_ptr<ManagedObj> intPtr(new ManagedObj[1], false);	// error 要自己加入引用計數
}

// main function
int main(void)
{
	// -----std::auto_ptr-----
	//testAutoPtr();
	//testAutoPtr2();
	//testAutoPtr3();

	// -----boost::scoped_ptr-----
	//testScopedPtr();

	// -----boost::shared_ptr-----
	//testSharedPtr1();

	// -----boost::scoped_array-----
	//testScopedArray();

	// -----boost::shared_array-----
	//testSharedArray1();

	// -----boost::weak_ptr-----
	testWeakPtr();

	return 0;
}


你說你會C++? —— 智能指針