您的位置:首页 > 其它

STL中的auto_ptr智能指针用法(转)

2010-09-19 23:20 465 查看
STL中的auto_ptr指针是为了解决内存泄漏问题而设计的。它严格限制了指针拥有对指向对象的所有权。auto_ptr指针和普通指针的差别在于对指向对象所有权的处理不同。auto_ptr指针是“传递”所有权,而普通指针是“共享”所有权。看下面例子:

std::auto_ptr<int> p1(new int(24));
std::auto_ptr<int> p2;
int *q1 = new int(12);
int *q2;
p2 = p1;
q2 = q1;
经过两次赋值后,对于auto_ptr,p1为NULL,*p2为24;对于普通指针,*p1, *p2均为12。第一次赋值,p1把指向对象的所有权传递给了p2, p1不再拥有指向对象的所有权。而第二次赋值,q2和q1共享了对同一对象的所有权。因此,对于auto_ptr,一个对象只可能被一个智能指针指向,这样可有效避免内存泄漏问题。但是同时会引起新问题。看下面例子:
template<class T>
void BadPrint(std::auto_ptr<T> p)
{
if (p.get() == NULL)
{
std::cout<<NULL;
}
else
{
std::cout<<*p;
}
}
然后我如下使用BadPrint函数:
std::auto_ptr<int> q(new int(18));
BadPrint(q);
*q = 12;
该程序并未像我们所期望的一样:*q的值为12,而是会出现runtime error,why?因为我们把q作为函数参数传给了BadPrint,因此传递完成后q不再拥有对指向对象的所有权,而函数内部的局部变量p会接管q所指向对象的所有权,当函数执行完毕退出时,p的生命期截止同时delete所指向对象。因此q实际变为NULL,所以出错。如何避免出错?使用 auto_ptr的引用?即 void BadPrint(std::auto_ptr<T> &p)。这是相当糟糕的一种做法。对智能指针使用引用混淆了所有权的问题。它导致所有权有可能被传递了,也可能没有被传递。无论如何应当避免对 auto_ptr使用引用。
可见智能指针并非对任何情况都智能。使用auto_ptr要知道:
1. 智能指针不能共享指向对象的所有权
2. 智能指针不能指向数组。因为其实现中调用的是delete而非delete[]
3. 智能指针不是万能的
4. 智能指针不能作为容器类的元素。例如:
template<class T>
void container::insert(const T &value)
{
..........
X = value;
..........
}
事实上在stl中,所有的container要内部拷贝所传参数的值时都是传的 const类型的值。因此无法用auto_ptr传进去。

其实现代码如下,注释详解了其功能用法:

/*
auto_ptr模板类
唯一成员数据是一个模板类型指针

作用:动态分配对象以及当对象不再需要时自动执行清理

int* p = new int(0);
auto_ptr<int> ap(p);
从此不必关心应该何时释放p,也不用担心发生异常会有内存泄漏
方式:
1.auto_ptr的做法是“所有权转移”,即拷贝或赋值的源对象将失去对“裸”指针的所有权,所以,与一般拷贝构造函数,赋值函数不同
2.拷贝或赋值的目标对象将先释放其原来所拥有的对象
注意:
1.因为auto_ptr析构的时候肯定会删除他所拥有的那个对象,两个auto_ptr不能同时拥有同一个对象
2.auto_ptr的析构函数中删除指针用的是delete,而不是delete [],所以不应该用auto_ptr来管理一个数组指针
3.构造函数的explicit关键词有效阻止从一个“裸”指针隐式转换成auto_ptr类型
*/

template<typename _Tp> class auto_ptr
{
private:
_Tp* _M_ptr;

public:
typedef _Tp element_type;

//构造函数
//explicit表示该构造函数禁止隐式类型转换,即传入的参数必须是_Tp*类型
explicit auto_ptr(element_type* __p = 0) throw() : _M_ptr(__p) {}

//同类型拷贝构造函数,参数是另一个auto_ptr,但该auto_ptr要释放自己包含的指针
//这是一个move(因此:千万不要使用类型为auto_ptr的容器)
auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) {}

//不同类型的拷贝构造函数
//也是move
template<typename _Tpl>
auto_ptr(auto_ptr<_Tpl>& __a) throw() : _M_ptr(__a.release()) {}

//同类型auto_ptr赋值操作符,__a释放自己包含的指针
//本模板类用reset更新自己的指针为__a指针
auto_ptr& operator=(auto_ptr& __a) throw()
{
reset(__a.release());
return *this;
}

//不同类型的auto_ptr赋值操作符
template<typename _Tpl>
auto_ptr& operator=(auto_ptr<_Tpl>& __a) throw()
{
reset(__a.release());
return *this;
}

//析构函数
//仅支持delete,不支持delete[]
~auto_ptr() { delete _M_ptr; }

//重载dereference操作符
element_type& operator* const throw()
{
_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
return *_M_ptr;
}

//重载->操作符
element_type* operator->() cosnt throw()
{
_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
return _M_ptr;
}

//获取成员指针函数
element_type* get() const throw() { return _M_ptr; }

//获取成员指针函数,同时将成员指针清零
element_type* release() throw()
{
element_type* __tmp = _M_ptr;
_M_ptr = 0;
return _tmp;
}

//释放成员指针所指对象,同时给成员指针赋新值
void reset(element_type* __p = 0) throw()
{
if(__p != _M_ptr)
{
delete _M_ptr;
_M_ptr = __p;
}
}

//从auto_ptr_ref构造auto_ptr的构造函数
auto_ptr(auto_ptr_ref<element_type> __ref) throw() : _M_ptr(__ref._M_ptr) { }

//从auto_ptr_ref的赋值操作符
//因为这里要释放内存,所以要进行self-assignment检查
auto_ptr& operator=(auto_ptr_ref<element_type> __ref) throw()
{
if(__ref._M_ptr != this.get())
{
delete _M_ptr;
_M_ptr = __ref._M_ptr;
}
return *this.
}

//本auto_ptr到其他任意类型auto_ptr_ref的conversation操作符重载
template<typename _Tp1>
operator auto_ptr_ref<_Tp1>() throw()
{
return auto_ptr_ref<_Tp1>(this->release());
}

};

// 模拟auto_ptr的reference类型
//可被一个传回auto_ptr值的函数赋值
template<typename _Tp1> struct auto_ptr_ref
{
_Tp1* _M_ptr;
explicit auto_ptr_ref(_Tpl* __p) : _M_ptr(__p) { }
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: