包含头文件 #include <memory>
名字空间 namespace std {}
我们细细来看,auto_ptr其实是用一个类实现了指针的功能,那么:
1. 这个类到底具有什么功能。
2. 它设计的初衷是什么?
3. 它有哪一些不能做的东西?
从类的private成员开始,
template<class Y>
......
private:
T* ap; // refers to the actual owned object (if any)
....
可以看到,这是一个模板类,包含的对象其实就是我们平常用的指针,然后肯定就是赋予这个类型的指针更强大的功能(那是必须的,不然要这个类有什么用)。
构造函数:
// constructor
explicit auto_ptr (T* ptr = 0) throw()
: ap(ptr) {
}
那么这个显式才能调用的构造函数其实就是将一个T类型的普通指针赋值给其私有变量了,如:
float * pt = new float(5.);
auto_ptr<float> apt(pt);
delete pt; // could we do this??
那么如果我们用 delete pt来删除pt和apt指向的内存会怎么样呢,试了一下,会报错,原因应该是apt在出作用域的时候会自动调用析构函数,会导致重复删除内存空间。
接下来看看复制构造函数和赋值构造函数是什么样的,从这两个函数可以对这个类的特性做一些推断(此方法叫做管中窥豹):
// 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()) {
}
注意到这有两个复制构造函数,这是什么意思呢?这意味着我们可以从别的类型来进行复制操作(难道牵涉到类的多态?),还有一点,参数中都是不带const限定符的,是否意味着我们需要对输入的参数进行一些改变?我们来看看release()函数:
// release ownership
T* release() throw() {
T* tmp(ap);
ap = 0;
return tmp;
}
这个代码就非常有意思了,看注释的意思是释放了所有权,里面代码显示 ap = 0 意味着指向了NULL,但是呢,并没有释放它之前指向的内存空间,只是将这个地址呢通过返回值传出去了(这就是待转移的意思)。那么我们可以推断使用复制构造函数之后,原来的对象指向NULL区域了,这跟我们普通类型的复制并不相同,(这是否意味着只有一个指针指向这片内存空间,与我们后面将要学习的shared_ptr似乎有点区别)。那么看第二个复制构造函数明显就是为了继承类指针向基类指针复制所准备的。
那么接下来再看赋值构造函数,跟复制构造函数是一样样的,
// 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;
}
// reset value
void reset (T* ptr=0) throw() {
if (ap != ptr) {
delete ap;
ap = ptr;
}
}
那么这个赋值构造函数完成了这么个事儿:将待赋值的对象所指向的空间给清理了,代码在 reset 函数中的delete 这一行,同时呢,将 = 右边的这个赋值对象的地址给了NULL值,相当于剥夺了它的控制权,同时将它原本指向的空间给了 = 左边这个待赋值的对象。
接下来就是一些让用户使用的public函数,让使用这个对象的体验像传统的指针:
// 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;
}
行文至此,你可能觉得要结束了,但是我居然碰到了最大的疑惑。。我发现了这个类里面居然还有这么一个东西,这是什么,为什么要一个auto_ptr_ref这么一个结构体呢?
// 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) {
}
};
/* 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());
}
这三个函数似乎是在完成一个中间驿站的功能,第三个函数定义一个转换函数,可以将auto_ptr<T> 类型转换成auto_ptr_ref<Y>的结构体(为什么是<Y>呢,为什么不在第三个函数定义中省略掉template<class Y>),然后利用构造函数auto_ptr( auto_ptr_ref<T> & ) 生成一个auto_ptr<T>的对象,这是为什么呢?考虑到下面两种情况:
auto_ptr<int> apt1 = auto_ptr<int>( new int(5) );
auto_ptr<int> apt2 = fun();
auto_ptr<int> fun()
{
....
}
这两种情况都会首先构造一个临时对象(不能寻址的右值),但是呢,右值是不能够作为复制或者赋值构造函数的参数的,除非该参数有const &标志,但是恰好auto_ptr类的复制、赋值构造函数都是非常量类型引用的(为了满足auto_ptr单一所有权的要求),所以这时情况就变得难办了。(尴尬!)怎么办呢?也许你会说用传值复制/赋值函数吧,别传引用了,但是你想呀,在传值的过程中首先就要调用复制构造函数,这就会导致无限递归调用,所以这种方法不可行!!
在这种情况下,我们就考虑分两步来解决问题,首先将临时的 auto_ptr 对象转换成 auto_ptr_ref 结构体,这里面就使用了operator 开头的转换函数,然后呢,其实得到的还是一个临时变量,只不过这个临时变量是 auto_ptr_ref 类型了,最后,将这个临时变量通过传值的方式传入相应的复制/赋值构造函数中!(注意第一和第二个函数是传值调用的,不是我们通常的传引用调用)
最后一个函数:
template<class Y>
operator auto_ptr<Y>() throw() {
return auto_ptr<Y>(release());
}
我的猜想是让这个类保持指针操作的连续性,因为指针中类型之中也可以相互转化?或者不能转化,那放在auto_ptr中情况是一样的
float * to int *???
auto_ptr<float> to auto_ptr<int> ???
应该说这是任意转换?而继承类指针到基类指针的转换是通过前面两个复制/赋值构造函数完成的,大概这么多先,源代码在下面:
/* 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());
}
};
}