auto_ptr作爲成員之一時的使用及具體特性的代碼實現——STL-record02

目錄

 

auto_ptr作爲成員之一的應用

auto_ptr的錯誤運用

auto_ptr之間不能共享擁有權

並不存在針對array而設計的auto_ptrs

auto_ptrs絕非一個“四海通用”的智能型指針

auto_ptrs不滿足STL容器對其元素的要求

auto_ptrs的忠告

auto_ptr的運用實例

auto_ptr的實作細目


auto_ptr作爲成員之一的應用

使用auto_ptr的一個最關鍵理由,就是使用了auto_ptr可以避免遺失資源。使用auto_ptr作爲成員,當對象被刪除時,auto_ptr也會自動刪除其所指向的成員對象。

另外,在對象初始化的時候,auto_ptr也有其優勢,按照普遍的new和delete的做法,只有當對象通過new的方式創建成功了,纔可以進行後一步的delete,那如果在new的時候沒創建成功,但同時有真的發生了佔據內存的操作時,就無法析構佔據的空間了。造成了內存資源的泄漏。但是,使用auto_ptr進行創建的時候,在對象初始化期間如果拋出異常,auto_ptr也可以幫助避免資源的遺失,這是new和delete搭配所做不到的。

比如,下面是一個按照普遍的new和delete的做法,進行的一個函數設計:

class ClassB {
private:
	ClassA* ptr1;
	ClassA* ptr2;
public:
	ClassB(ClassA vall, ClassA val2)
		:ptr1(new ClassA(val1)), ptr2(new ClassA(val2)) //構造函數:通過new,進行參數的初始化操作
	{
	}

	ClassB(const ClassB& x)
		:ptr1(new ClassA(*x.ptr1)), ptr2(new ClassA(*x.ptr2)) //拷貝構造函數:依舊是通過new,進行參數的初始化操作
	{
	}

	const ClassB& operator= (const ClassB& x) //賦值操作符的重載
	{
		*ptr1 = *x.ptr1;
		*ptr2 = *x.ptr2;
		return *this;
	}

	~ClassB() {
		delete ptr1;
		delete ptr2;
	}
};

這種設計方式帶來的問題,之前說過了,就是,如果第一個new成功了,但第二個new卻失敗了,就會造成資源的遺失。只有當所有的new都創建成功了纔可以,但凡有一個沒成功,那所有的就都斷了,已經創建的也無法再析構了。

再看使用auto_ptr來進行初始化,可以避免上面的問題:

class ClassB {
private:
	const std::auto_ptr<ClassA> ptr1;
	const std::auto_ptr<ClassB> ptr2;
public:
	ClassB(ClassA val1, ClassA val2)
		:ptr1(new ClassA(val1)), ptr2(new ClassA(val2))//構造函數,使用auto_ptr初始化
	{}

	ClassB(const ClassB& x)
		:ptr1(new ClassA(*x.ptr1)),ptr2(new ClassA(*x.ptr2))//拷貝構造函數,使用auto_ptr初始化
	{}

	const ClassB& operator= (const ClassB& x)//賦值的操作符的重載
	{
		*ptr1 = *x.ptr1;
		*ptr2 = *x.ptr2;
		return *this;
	}
};

但是,需要強調的是,即便auto_ptr可以實現不依靠析構函數來進行析構,但拷貝構造和賦值操作符重載也是需要進行重新編寫的。如果不重寫,那編譯器會自己添加,那麼,這兩個操作都會轉交擁有權,這恐怕就不一定是編程的人的意願了。所以,如果在整個生命週期內的auto_ptr都不想改變其所指向對象的擁有權。最好還是可以選擇const auto_ptr,來進行約束。

auto_ptr的錯誤運用

auto_ptr之間不能共享擁有權

一個auto_ptr千萬不能指向另一個auto_ptr(或其他對象)所擁有的對象。否則,當第一個指針刪除該對象後,另一個指針突然間指向了一個已被銷燬的對象,那麼,如果再使用那個指針進行讀寫操作,就會引起一場災難。

並不存在針對array而設計的auto_ptrs

auto_ptr不可以指向array,這是因爲auto_ptr的本質還是通過delete來進行刪除的,而不是delete[]來釋放其所擁有的對象的。而且,C++標準庫中並未提供針對array而設計的auto_ptr。當然,標準庫會另外提供一些容器來管理數據羣。

auto_ptrs絕非一個“四海通用”的智能型指針

並非是任何適合智能型指針的地方,都適合auto_ptr。特別提醒的是,如果是引用計數型的指針,如果有一組智能型指針指向同一個對象,那麼,當且僅當最後一個智能型指針被銷燬時,該對象纔會被銷燬。故這種場景下,並不適合用auto_ptr來指向。

auto_ptrs不滿足STL容器對其元素的要求

比如,在拷貝動作和賦值動作之後,原本的auto_ptr和新產生的auto_ptr並不相等。原本的auto_ptr會交出控制權,而不是普遍意義上的拷貝給新的auto_ptr,所以,這個地方的拷貝和賦值函數就需要重新的定義了。一般的適用於其他元素的拷貝和賦值操作,就不適合auto_ptr所指向的類型。

auto_ptrs的忠告

有時候使用指向一個非常數的auto_ptr,並不比使用一個一般指針更安全。就比如當遇到引用計數型的場景的時候,就反而非常糟糕。而當我們有必要在不同容器之間共享元素時,這種指針非常有用了就。

auto_ptr的運用實例

根據之前的記述,下面對auto_ptr進行一個實際的操作,深切的體會到auto_ptr類似於一個常量指針,允許修改其所指向的內容,但不能修改其指針變量本身,下面這個展現auto_ptrs的轉移所有權的行爲:

#include <iostream>
#include <memory>
using namespace std;

template <class T>
ostream& operator<< (ostream& strm, const auto_ptr<T>& p)
{
	if (p.get() == NULL)
	{
		strm << "NULL";
	}
	else
	{
		strm << *p;
	}
	return strm;
}

int main()
{
	auto_ptr<int> p(new int(42));
	auto_ptr<int> q;

	cout << "after initialization:" << endl;
	cout << "p:" << p << endl;
	cout << "q:" << q << endl;

	q = p;
	cout << "after assigning auto pointers:" << endl;
	cout << "p:" << p << endl;
	cout << "q:" << q << endl;

	*q += 13;
	p = q;
	cout << "after change and reassignment:" << endl;
	cout << "p:" << p << endl;
	cout << "q:" << q << endl;
	system("pause");
}

運行結果:

下面再看,展示const auto_ptr的特性的例子:

#include <iostream>
#include <memory>
using namespace std;

/* define output operator for auto_ptr
 * - print object value or NULL
 */
template <class T>
ostream& operator<< (ostream& strm, const auto_ptr<T>& p)
{
	// does p own an object ?
	if (p.get() == NULL) {
		strm << "NULL";         // NO: print NULL
	}
	else {
		strm << *p;             // YES: print the object
	}
	return strm;
}

int main()
{
	const auto_ptr<int> p(new int(42));
	const auto_ptr<int> q(new int(0));
	const auto_ptr<int> r;

	cout << "after initialization:" << endl;
	cout << " p: " << p << endl;
	cout << " q: " << q << endl;
	cout << " r: " << r << endl;

	*q = *p;
	//  *r = *p;    // ERROR: undefined behavior
	*p = -77;
	cout << "after assigning values:" << endl;
	cout << " p: " << p << endl;
	cout << " q: " << q << endl;
	cout << " r: " << r << endl;

	//  q = p;      // ERROR at compile time
	//  r = p;      // ERROR at compile time
	system("pause");
}

運行結果:

auto_ptr的實作細目

這裏來記錄下auto_ptr的實現機理:

/* class auto_ptr
 * - improved standard conforming implementation
 */
namespace std {
	// auxiliary type to enable copies and assignments (now global)
	template<class Y>
	struct auto_ptr_ref {
		Y* yp;
		auto_ptr_ref(Y* rhs)
			: yp(rhs) {
		}
	};

	template<class T>
	class auto_ptr {
	private:
		T* ap;    // refers to the actual owned object (if any)
	public:
		typedef T element_type;

		// constructor
		explicit auto_ptr(T* ptr = 0) throw()
			: ap(ptr) {
		}

		// copy constructors (with implicit conversion)
		// - note: nonconstant parameter
		auto_ptr(auto_ptr& rhs) throw()
			: ap(rhs.release()) {
		}
		template<class Y>
		auto_ptr(auto_ptr<Y>& rhs) throw()
			: ap(rhs.release()) {
		}

		// assignments (with implicit conversion)
		// - note: nonconstant parameter
		auto_ptr& operator= (auto_ptr& rhs) throw() {
			reset(rhs.release());
			return *this;
		}
		template<class Y>
		auto_ptr& operator= (auto_ptr<Y>& rhs) throw() {
			reset(rhs.release());
			return *this;
		}

		// destructor
		~auto_ptr() throw() {
			delete ap;
		}

		// value access
		T* get() const throw() {
			return ap;
		}
		T& operator*() const throw() {
			return *ap;
		}
		T* operator->() const throw() {
			return ap;
		}

		// release ownership
		T* release() throw() {
			T* tmp(ap);
			ap = 0;
			return tmp;
		}

		// reset value
		void reset(T* ptr = 0) throw() {
			if (ap != ptr) {
				delete ap;
				ap = ptr;
			}
		}

		/* special conversions with auxiliary type to enable copies and assignments
		 */
		auto_ptr(auto_ptr_ref<T> rhs) throw()
			: ap(rhs.yp) {
		}
		auto_ptr& operator= (auto_ptr_ref<T> rhs) throw() {  // new
			reset(rhs.yp);
			return *this;
		}
		template<class Y>
		operator auto_ptr_ref<Y>() throw() {
			return auto_ptr_ref<Y>(release());
		}
		template<class Y>
		operator auto_ptr<Y>() throw() {
			return auto_ptr<Y>(release());
		}
	};
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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