C++标准程序库提供的auto_ptr是一种智能型指针,帮助程序员防止“被异常抛出时发生资源泄漏”。注意我说的是“一种”智能型指针,现实生活中还有其他许多有用的智能型指针,auto_ptr只是针对某个特定问题而设计的,对于其他问题,auto-ptr无能为力。
一、auto_ptr的设计动机:
函数的操作经常需要依以下模式进行:
1、获取一些资源。
2、执行一些操作。
3、释放所获得的资源。
假如函数一开始获得的资源被绑定在局部对象身上,当函数退出时,他们的析构函数被调用,从而自动释放这些资源。但是如果函数一开始获得的资源是以显式手法(比如说:new)获得,那就必须以显示手法(比如说:delete)释放。但是有时候会忘记delete或者异常一旦发生,函数立刻退出,根本执行不到函数尾端的delete语句,这就可能造成内存泄漏。当然你可以在函数中实现捕捉所有异常。但会使得函数变得十分复杂且容易出错。
如果使用智能型指针能保证在任何一种情况下,只要自己被摧毁,就一定连带释放其所指的资源(因为智能型指针本来就是局部变量,所以无论是正常退出,还是异常退出,都能保证该指针被销毁从而释放资源)。
注意点:
1、auto_ptr是这样一种指针:他是“他所指向的对象”的拥有者。所以,当身为对象拥有者的auto_ptr被摧毁时,该对象也将遭到摧毁。auto_ptr要求一个对象只能有一个拥有者。不再需要delete,也不再需要catch。
2、auto_ptr拥有权是可以转移的:
std::auto_ptr<ClassA> ptr1(new ClassA);
std::auto_ptr<ClassA> ptr2(ptr1);
或者
std::auto_ptr<ClassA> ptr1(new ClassA);
std::auto_ptr<ClassA> ptr2;
ptr2 = ptr1;
在第一个语句中,ptr1拥有了那个new出来的对象。在 第二个语句中,拥有权由ptr1转交给ptr2。此后ptr2即拥有了那个new出来的对象,而ptr1不在拥有他。这样,对象就只会被delete一次(在ptr2被摧毁时)。
如果ptr2在转交拥有权之前拥有另一个对象,则转交操作执行之前会先调用delete,将ptr2所拥有的对象先摧毁。
std::auto_ptr<ClassA> ptr1(new ClassA);
std::auto_ptr<ClassA> ptr2(new ClassA);
ptr2 = ptr1; //ptr2之前拥有的对象被销毁,ptr1将第一个new出来的对象的拥有权转交给ptr2,ptr1为NULL。
特殊用法:某个函数可以利用auto_ptr将拥有权转交给另一个函数。这种情况可能出现在两种情况:
a、auto_ptr被以传值的方式被当作一个参数传递给某个函数。此时被调用函数的参数就获得了这个auto_ptr的拥有权。
void sink(std::auto_ptr<ClassA>);
b、当auto_ptr被当作返回值返回时,将拥有权转交给调用端。
std::auto_ptr<ClassA> fun()
{
std::auto_ptr<ClassA> ptr(new ClassA);
...
return ptr;
}
void gun()
{
std::auto_ptr<ClassA> ptr;
for (int i=0; i<10; i++)
{
p = fun(); //一旦进入循环再次执行赋值动作,意味着ptr先前拥有的对象被销毁了。
...
}
}
3、const std::auto_ptr<class T> ptr;ptr的拥有权不允许被转移。在这里,关键词const并非意味着你不能更改auto_ptr所拥有的对象,而是意味着你不能更改auto_ptr的拥有权。例如:
std::auto_ptr<int> f()
{
const auto_ptr<int> p(new int);
auto_ptr<int> q(new int);
*p = 42; //ok
q = p; //error
p = q; //error
return p; //error
}
4、并不存在针对array设计的auto_ptr。因为auto_ptr是透过delete而非delete[]来释放其所拥有的对象。
5、auto_ptr不满足STL标准容器的对其元素的要求。因为在拷贝和赋值动作之后,原本的auto_ptr和新产生的auto_ptr并不相等。原本的auto_ptr会交出拥有权,而不是拷贝给新的auto_ptr。
6、auto_ptr不是引用计数型的指针——这种指针保证,如果有一组智能型指针指向同一个对象,那么当且仅当最后一个智能型指针被注销时,该对象才会被摧毁。
运用实例:
#include <iostream>
#include <memory>
using namespace std;
template <class T>
ostream& operator<< (ostream& strm, const auto_ptr<T>& p)
{
if (NULL == p.get())
{
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;
return 0;
}
注意:output操作符的第二个参数是一个const refernce,所以并没有发生拥有权的转移。
7、不能以一般指针的赋值手法来初始化一个auto_ptr:
std::auto_ptr<int> p(new int(42)); //OK
std::auto_ptr<int> p = new int(24); //ERROR
p = std::auto_ptr<int>(new int(24)); //OK
p = new int(24); // ERROR
下面一个例子展示const auto_ptr的特性:
#include <iostream>
#include <memory>
using namespace std;
template <class T>
ostream& operator<< (ostream& strm, const auto_ptr<T>& p)
{
if (NULL == p.get())
{
strm << "NULL";
}
else
{
strm << *p;
}
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:undefine behavior
*p = -77;
cout << "after assigning auto pointers:" << 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;
}
注意下列赋值操作是错误的:
*r = *p;
这个句子对于一个“未指向任何对象”的auto_ptr进行提领操作。C++标准规定,这会导致未定义行为,比如说导致程序的崩溃。