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());
		}
	};
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

おすすめ

転載: blog.csdn.net/qq_17846375/article/details/105357135