《C++标准程序库》第四章摘录与笔记

《C++标准程序库》第四章摘录与笔记

4.1 Pairs(对组)

类pair可以将两个对象视为一个对象。容器类别map和multimap使用pairs来管理器键/值对。任何函数需返回两个值,也需要pair。

4.2 Class auto_ptr

C++标准程序库提供的auto_ptr是“一种”智能指针,帮助程序员防止“被异常抛出时发生资源泄露”。auto_ptr只是针对特定问题而设计的,对于其他问题,他无能为力。

4.2.1 auto_ptr的设计动机

对于对象指针,如果对象的资源是以显示方法获得,而且没有被绑定在任何对象上,那就必须以显示方法释放。但如果在释放前return或者发生异常,则可能内存泄露或者说资源遗失。一种方法是可以通过捕获所有异常来防止这种资源遗失,如:
void f()
{
	ClassA *ptr = new ClassA;	// create an object explicitly
	
	try {
		...		// perform some operations
	}
	catch(...) {	// for any exception
		delete ptr;	// clean up
		throw;		// rethrow the exception
	}


	delete ptr;		// clean up on normal end
}
这样处理显得复杂且容易出错。
另一种方法是使用智能指针。auto_ptr这种职能指针保证,无论在任何情况下,只要自己被摧毁,就一定连带释放其所指资源。而由于智能指针本身就是区域变量,所以无论是正常退出,还是异常退出,只要函数退出,它就一定会被摧毁。auto_ptr是“它所指对象”的拥有者,所以当auto_ptr被摧毁时,该对象也将遭到摧毁。auto_ptr要求一个对象只能有一个拥有者,严禁一物二主。(绝对不能使用同一个对象初始化多于一个的auto_ptr!!!)
《MSDN》摘录
The template class describes an object that stores a pointer to an allocated object of type Type* that ensures that the object to which it points gets destroyed automatically when control leaves a block.
The template class describes an object that stores a pointer to an allocated object myptr of type Type *. The stored pointer must either be null or designate an object allocated by a new expression. An object constructed with a nonnull pointer owns the pointer. It transfers ownership if its stored value is assigned to another object. (It replaces the stored value after a transfer with a null pointer.) The destructor for auto_ptr<Type> deletes the allocated object if it owns it. Hence, an object of class auto_ptr<Type> ensures that an allocated object is automatically deleted when control leaves a block, even through a thrown exception. You should not construct two auto_ptr<Type> objects that own the same object.
You can pass an auto_ptr<Type> object by value as an argument to a function call. You can return such an object by value as well. Both operations depend on the implicit construction of intermediate objects of class auto_ptr<Type>::auto_ptr_ref<Other>, by various subtle conversion rules. You cannot, however, reliably manage a sequence of auto_ptr<Type> objects with a Standard Template Library container.
注意,auto_ptr<>不允许你使用一般指针惯用的赋值初始化方式,你必须直接使用数值来完成初始:
std::auto_ptr<ClassA> ptr1(new ClassA); // OK 
std::auto_ptr<ClassA> ptr2 = new ClassA; // ERROR!

4.2.2 auto_ptr拥有权的转移

auto_ptr的拷贝构造函数和赋值操作符的作用不是讲此处的数据拷贝到别处,而是进行了auto_ptr拥有权的转移!这样先前的拥有者是去了拥有权,只有null指针在手了,结果拷贝构造函数和赋值操作都带有副作用(对源对象进行了修改),这就要求程序员在之后不能再使用已经交出了拥有权的auto_ptr了。
只有auto_ptr可以拿来当做另一个auto_ptr的初值,普通指针不行(这是因为“根据一般指针生成一个auto_ptr”的那个构造函数被声明为explicit了):
std::auto_ptr<ClassA> ptr; // create an auto_ptr
ptr = new ClassA; // ERROR
ptr = std::auto_ptr<ClassA>(new ClassA); // OK, delete old object and own new
起点和终站
拥有权的转移,使得auto_ptr产生了一种特殊用法:某个函数可以利用auto_ptr将拥有权转交给另一个函数。这种事情可能在两种情况出现:
1. 某个函数是数据的终点。如果auto_ptr以值传递方式被当做一个参数传递给另外一个函数。
2. 某个函数式数据的起点。当一个auto_ptr被返回,其拥有权便被转交给调用端了。
缺陷
auto_ptr的语义本身就包含了拥有权,所以如果你无意转交你的拥有权,就不要在参数列中使用auto_ptr,也不要以它作为返回值。
可以使用constant reference来使用auto_ptr来使其无法交出拥有权,这样会导致转移所有权的操作(如拷贝构造函数和赋值操作)在交出拥有权时出现编译错误。总而言之,常量型auto_ptr减小了“不经意转移拥有权”所带来的危险。只要一个对象通过auto_ptr传递,就可以使用常量型auto_ptr来终结拥有权转移链,此后拥有权将不能再进行转移。这里的const并非意味你不能更改auto_ptr所拥有的对象,而是意味你不能更改auto_ptr的拥有权,与指针常量类似。
const std::auto_ptr<int> p(new int); // no ownership transfer possible
如果使用const auto_ptr作为参数,对新对象的任何赋值操作都将导致编译器错误。

4.2.3 auto_ptr作为成员之一

在class中使用auto_ptr,你可以因而避免遗失资源。如果你以auto_ptr而非一般指针作为成员,当对象被删除时,auto_ptr会自动删除其所指的成员对象,于是你也就不再需要析构函数了。此外,即使在初始化期间抛出异常,auto_ptr也可以帮助避免资源遗失。注意,只有当对象被完整构造成功,才有可能于将来调用其析构函数。这就造成了资源遗失的隐忧:如果第一个new成功了,第二个new却失败了,就造成了资源遗失。如:
class ClassB
{
private:
	ClassA *ptr1;	// pointer member
	ClassA *ptr2;
public:
	// constructor that initializes the pointers, will cause resource leak if second new throws
	ClassB(ClassA val1, ClassA val2)
	: ptr1(new ClassA(val1)), ptr2(new ClassA(val2)) {
	}


	 // copy constructor, might cause resource leak if second new throws
	ClassB(const ClassB& x)
	: ptr1(new ClassA(*x.ptr1)), ptr2(new ClassA(*x.ptr2)) {
	}


	// assignment operator
	const ClassB& operator=(const ClassB& x)
	{
		*ptr1 = *x.ptr1;
		*ptr2 = *x.ptr2;
		return *this;
	}


	~ClassB()
	{
		delete ptr1;
		delete ptr2;
	}
	// ...
};
使用auto_ptr则可以避免资源遗失:
class ClassB
{
private:
	const std::auto_ptr<ClassA> ptr1;	// auto_ptr member
	const std::auto_ptr<ClassA> ptr2;
public:
	// constructor that initializes the auto_ptrs, no resource leak possible
	ClassB(ClassA val1, ClassA val2)
	: ptr1(new ClassA(val1)), ptr2(new ClassA(val2)) {
	}


	 // copy constructor, no resource leak possible
	ClassB(const ClassB& x)
	: ptr1(new ClassA(*x.ptr1)), ptr2(new ClassA(*x.ptr2)) {
	}


	// assignment operator
	const ClassB& operator=(const ClassB& x)
	{
		*ptr1 = *x.ptr1;
		*ptr2 = *x.ptr2;
		return *this;
	}


	// no destructor necessary
	// (default destructor let ptr1 and ptr2 delete their objects)
	// ...
};
但是要注意,由于缺省情况下,拷贝构造函数和赋值操作会导致auto_ptr转移拥有权,所以需要自己实现这两个成员函数。为了避免拥有权的转交,如果你的auto_ptr在整个生命周期中不必改变其所指对象的拥有权,你可以使用const auto_ptr。

4.2.4 auto_ptr的错误运用

C++标准程序库中只有auto_ptr这一个智能指针。使用它的一些要点:
1. auto_ptrs直接不能共享拥有权
2. 并不存在针对array而设计的auto_ptrs。auto_ptr不可以指向array,因为auto_ptr是透过delete而非delete[]来释放其所拥有的对象。
3. auto_ptrs绝非一个“四海通用”的智能型指针。特别注意的是,它不是引用计数型指针!(第一条就可以发现了)“引用计数型智能指针”,在我们有必要在不同容器之间共享元素时非常有用!
4. auto_ptrs不满足STL容器对其元素的要求。还是由于拥有权移交时的副作用。所以请绝对不要将auto_ptr作为标准容器的元素!

4.2.5 auto_ptr运用实例

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


// define output operator for auto_ptr
// print object value or NULL
// 第二个参数是一个const reference,所以没有发生auto_ptr拥有权的转移!
template <typename 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()
{
	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 << "agter assigning auto pointers: " << endl;
	cout << "p: " << p << endl;
	cout << "q: " << q << endl;


	*q += 13;	// change value of the object q owns
	p = q;	
	cout << "after change and reassignment: " << endl;
	cout << "p: " << p << endl;
	cout << "q: " << q << endl;


	return 0;
}

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


// define output operator for auto_ptr
// print object value or NULL
// 第二个参数是一个const reference,所以没有发生auto_ptr拥有权的转移!
template <typename 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(runtime error)
	*p = -77;
	cout << "agter 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
	return 0;
}

4.2.6 auto_ptr实作细目

/**
 * class auto_ptr
 * -- improved standard conforming implementation
 */

// 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
{
public:
	// constructor
	explicit auto_ptr(T* p = 0);

	// copy constructor (with implicit conversion)
	// member template
	// 以任何兼容的auto_ptr作为一个新的auto_ptr的初值
	// --note: nonconstant parameter
	template <class U>
	auto_ptr(auto_ptr<U>& rhs);

	// destructor
	~auto_ptr();

	// assignment operator (with implicit conversion)
	// member template
	// 以任何兼容的auto_ptr作为赋值动作的右端
	// --note: nonconstant parameter
	template <class U>
	auto_ptr<T>& operator=(auto_ptr<U>& rhs);

	// value access
	T& operator*() const;
	T* operator->() const;
	T* get() const;			// 返回dumb pointer
	
	// release ownership
	T* release();			// 撤回dumb pointer的拥有权并返回其值
	
	// reset value
	void reset(T* p = 0);	// 将拥有的指针删除,并承担p的拥有权

// special conversions to enable copies and assignments
public:
	auto_ptr(auto_ptr_ref<T> rhs);
	auto_ptr<T>& operator=(auto_ptr_ref<T> rhs);
	template <class U> operator auto_ptr_ref<U>();
	template <class U> operator auto_ptr<U>();


private:
	T *pointee;	// refers to the actual owned object(if any)
	// 让所有的auto_ptr classes都成为另一个auto_ptr的friends
//	template <class U>
//	friend class auto_ptr<U>;
};

template <class T>
inline auto_ptr<T>::auto_ptr(T* p)
: pointee(p)
{}

template <class T>
	template <class U>
	inline auto_ptr<T>::auto_ptr(auto_ptr<U>& rhs)
	: pointee(rhs.release())
	{}

template <class T>
inline auto_ptr<T>::~auto_ptr()
{
	delete pointee;
}

template <class T>
	template <class U>
	inline auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr<U>& rhs)
	{
		if (this != &rhs)
			reset(rhs.release());
		return *this;
	}

template <class T>
inline T& auto_ptr<T>::operator*() const
{
	return *pointee;
}

template <class T>
inline T* auto_ptr<T>::operator->() const
{
	return pointee;
}

template <class T>
inline T* auto_ptr<T>::get() const
{
	return pointee;
}

template <class T>
inline T* auto_ptr<T>::release()
{
	T *oldPointee = pointee;
	pointee = 0;
	return oldPointee;
}

template <class T>
inline void auto_ptr<T>::reset(T *p)
{
	if (pointee != p)
	{
		delete pointee;
		pointee = p;
	}
}

// special conversion with auxiliary type to enable copies and assignments
template <class T>
inline auto_ptr<T>::auto_ptr(auto_ptr_ref<T> rhs)
: pointee(rhs.yp)
{}

template <class T>
inline auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr_ref<T> rhs)
{
	reset(rhs.yp);
	return *this;
}

template <class T>
	template <class U>
	inline auto_ptr<T>::operator auto_ptr_ref<U>()
	{
		return auto_ptr_ref<U>(release());
	}

template <class T>
	template <class U>
	inline auto_ptr<T>::operator auto_ptr<U>()
	{
		return auto_ptr<U>(release());
	}
参考了《More Effective C++》
在实现auto_ptr中,拷贝构造函数和赋值操作符参数都使用的是reference object而不是reference to const object。这是由于auto_ptr需要在拷贝时放弃拥有权,并且拷贝动作必须修改原始auto_ptr。而要修改原始auto_ptr,如果使用reference to const object,则auto_ptr中实际包含的指针必须声明为mutalbe,但是这样有会让用户可以那些声明为const的对象,将其所有权交给别人。所以将其设置为reference object。
#include <iostream>
#include "auto_ptr.h"

int main()
{
	auto_ptr<int> pi(new int(0));
	auto_ptr<char> pc(new char(49));
	const auto_ptr<int> cpi(new int(11));
	auto_ptr<int> api(new int(22));


	*pi = 10;
//	api.reset(pi.get());	// 在VS2005中运行时出现了错误,但是在GCC中没出现错误。
	std::cout << *pi << std::endl;
	std::cout << *pc << std::endl;
	std::cout << *cpi << std::endl;
	std::cout << *api << std::endl;

	return 0;
}
注释的这行在VS2005中都会导致运行时错误,因为出现了两个指针指向一个对象,这样释放两次就会出错,但是GCC中测试没有出错,单独在GCC中写一段测试代码释放掉同一动态申请的区域则会出错。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值