以下内容只要来自《C++标准库》这本书和网上资源:
1.auto_ptr
智能指针能保证,无论在何种情况下,只要自己被摧毁,就一定连带释放其所指资源。auto_ptr是这样的一种指针:它是“它所指向的对象”的拥有者。atuo_ptr要求一个对象只能有一个拥有者,严禁一物二主。不再需要delete,也不再需要catch了。不用担心忘掉delete动作,担心程序异常结束时内存遗失或者资源遗失,只要有智能指针auto_ptr.c++标准库提供的auto_ptr一种帮助程序员防止“被抛出异常时发生资源泄漏”的智能指针。
auto_ptr在头文件#include<memory> 中
auto_ptr没有所有的指针算术(包括++)运算。auto_ptr不允许你使用一般指针惯用的赋值初始化方式。 例如:
std::auto_ptr<ClassA> ptr1(new ClassA); //对的
std::auto_ptr<ClassA> ptr2 =new ClassA; //错的
2.auto_ptr拥有权的转移
std::auto_ptr<ClassA> ptr1(new ClassA);//ptr1拥有了那个new出来的对象。
std::auto_ptr<ClassA> ptr2(ptr1); //ptr2拥有了那个new出来的对象,ptr1不再拥有它,即拥有权的转移;
//这样对象就只会在ptr2被摧毁的时候被delete一次。
下面的赋值操作和上面的差不多:
std::auto_ptr<ClassA> ptr1(new ClassA);
std::auto_ptr<ClassA> ptr2;
ptr2=ptr1; //拥有权从ptr1转移到ptr2
2)如果ptr2被赋值之前正拥有另一个对象,赋值时将会delete掉那个对象。
std::auto_ptr<ClassA> ptr1(new ClassA);
std::auto_ptr<ClassA> ptr2(new ClassA);
ptr2=ptr1; //拥有权从ptr1转移到ptr2,ptr2的原来的对象被delete掉。
拥有权的转移,实质上并非只是被简单拷贝而已。只要发生了拥有权转移,先前的拥有者就失去了拥有权,结果拥有者一旦交出拥有权,就两手空空,只剩下一个null指针在手了。
只有auto_ptr可以拿来当做另一个auto_ptr的初值,普通指针是不行的。例如:
std::auto_ptr<ClassA> ptr ;//可以定义空的auto_ptr指针,对于智能指针,因为构造函数有默认值0
ptr =new ClassA; //错误,
ptr =std::auto_ptr<ClassA> (new ClassA);//正确被auto_ptr对象或者auto_ptr指针赋值,
3)某函数是数据的终点,auto_ptr以传值方式被当作一个参数传递给某函数时。被调用端的参数获得了这个auto_ptr的拥有权,如果函数不再将它传递出去,它所指的对象就会在函数退出时被删除。如:void sink(std::auto_ptr<ClassA> );
4) 某函数是数据的起点。
std::auto_ptr<ClassA> f()
{
std::auto_ptr<ClassA> ptr(new ClassA);
//........
return ptr;
}
void g()
{
std::auto_ptr<ClassA> p;
for (int i =0; i <10; i++) {
p=f(); //p获得f()返回对象的拥有权。每当f()被调用时,它都new一个对象,然后把该对象连同其拥有权一起返回给调用端。
//一旦循环再次执行这个赋值动作,p原先拥有的对象被删除。当离开g()时,p也被摧毁。
}
auto_ptr的语义本身就包含了拥有权,所以如果你无意转交你的拥有权,就不要在参数列表值中使用auto_ptr,也不要以它作为返回值。
下面的例子本来是想将auto_ptr所指对象的值打印出来,却可能引发了错误:
//a bad example
#include <iostream>
#include
<memory>
using namespace std;
template<class T>
void
bad_print(std::auto_ptr<T> p)
{
if (NULL
==p.get())
cout <<"NULL";
else
cout << *p;
}
int
main()
{
auto_ptr<int> p(new int);
*p
=42;
bad_print(p); //bad_print调用结束后,p所指向的对象被删除了。
*p
=18;//执行期错误,用g++编译时出现Segmentation fault (core dumped)(段错误)
return
0;
}
//a bad example
#include <iostream>
#include
<memory>
using namespace std;
template<class T>
void
bad_print(std::auto_ptr<T> p)
{
if (NULL
==p.get())
cout <<"NULL";
else
cout << *p;
}
int
main()
{
const auto_ptr<int> p(new int);
*p
=42;
bad_print(p); //编译期错误,无法变更constant reference的拥有权
*p
=18; //ok
return 0;
}
关键字const并非意味你不能更改auto_ptr所拥有的对象,而是意味着你不能更改auto_ptr的拥有权。例如:
std::auto_ptr<int> f()
{
const std::auto_ptr<int> p(new int);
std::auto_ptr<int> q(new int);
*p =42;//ok,改变值
bad_print(p);//编译期错误,不能变更拥有权
*p =*q; //ok;
p =q; //编译期错误
return p; //编译期错误
}
如果使用const auto_ptr作为参数,对新对象的任何赋值操作都将导致编译期错误。就其常数特性而言,const auto_ptr比较类似常数指针(T* const p),
而非指向常数的指针(const T* p),尽管语法上更像后者。
3.auto_ptr的正确运用:
1)auto_ptr之间不能共享拥有权
一个auto_ptr千万不能指向另一个auto_ptr(或其它对象)所拥有的对象,尽管语法上没问题,很多情况下可能也不会发生错误。但是,当一个指针删除该对象后,另一个指针突然间指向一个已被摧毁的对象,那么,如果再使用那个指针进行读写操作,就会引发一场灾难。
2)并不存在针对array而设计的auto_ptrs
auto_ptr不能指向array,因为auto_ptr是透过delete而非delete[ ]来释放其所拥有的对象。注意,c++标准程序库并未针对array而设计的auto_ptr。标准程序库另提供了数个容器类别,用来管理数据群。
3)auto_ptrs绝非一个“四海通用”的智能型指针
并非任何适用智能指针的地方都适用auto_ptr,特别请注意的是,它不是引用计数型指针——这种指针保证,如果有一组智能型指针指向同一个对象,那么当且仅当最后一个智能型指针被销毁时,该对象才会被销毁。
4)auto_ptrs不满足STL容器对其他元素的要求
auto_ptr并不满足STL标准容器对于元素的最基本要求,因为在拷贝和赋值动作之后,原本的auto_ptr和新产生的auto_ptr并不相等。是的,拷贝和赋值之后,原本的auto_ptr会交出拥有权,而不是拷贝给新的auto_ptr。因此绝对不要将auto_ptr作为标准容器的元素。
4. auto_ptr的运用实例:
下面的例子展示autos_ptr转移的拥有权:
#include <iostream>
#include
<memory>
using namespace std;
//define output operator for
auto_ptr
template <class T>
ostream & operator <<
(ostream& strm,const auto_ptr<T>& p)
{
if (p.get()
==NULL) //get()返回auto_ptr指向的那个对象的内存地址
strm <<
"NULL";
else
strm << *p;
return strm;
}
int main()
{
auto_ptr<int> p(new
int(42));
auto_ptr<int> q;
cout <<
"afer initialization:" <<endl;
cout << " P: " << p
<<endl;
cout << " q: " << q
<<endl;
q =p; //改变p的拥有权
cout << "afer
assigning auto pointers::" <<endl;
cout << " P: " <<
p <<endl;
cout << " q: " << q
<<endl;
*q +=13;
p = q;
cout
<< "afer change and reassignment:" <<endl;
cout << "
P: " << p <<endl;
cout << " q: " << q
<<endl;
return 0;
}
//输出:
afer initialization:
P:
42
q: NULL
afer assigning auto pointers::
P:
NULL
q: 42
afer change and reassignment:
P: 55
q:
NULL
//注意:<<操作符的第二个参数是一个const参数,所以并没有发生拥有权的转移。
//实例2:展示const auto_ptr的特性:
#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" <<endl;
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: underfined
behavior
*p =-78;
cout << "after assigning values:"
<< endl;
cout << " p: " << p <<
endl;
cout << " q: " << q <<endl;
cout
<< " r: " <<r <<endl;
//p =q; //ERROR at compile
time
//r =p; //ERROR at compile time
return
0;
}
//输出:
after initialization:
p:
42
q: 0
r: NULL
after assigning values:
p:
-78
q: 42
r: NULL
//*r =*p;
这个句子对于一个“未定义对象”的auto_ptr进行提领(dereference)操作
//c++ 标准规定,这会导致未定义错误
一般定义的raw指针是这样的使用 class CBase;
class Derive : public CBase {
CBase *pa = new Derive;//如果这里发生异常 比如没有足够的内存分配 pa->调用相关成员函数;//
delete pa;//则不会执行到这里的删除指针内存的操作 发生内存泄漏 }
这样不好,如果有异常 会导致内存泄漏
如果像下面这样写 利用auto_ptr 类指针对象,当对象超过生存期时 会自动调用auto_ptr的析构函数来释放内存,安全比较好 #include "stdafx.h" #include <iostream> #include <algorithm> using namespace std; class CBase {
public: CBase(){}; ~CBase(){};
virtual void Print(){}; };
class Derive : public CBase {
public:
Derive(){};
Derive(int x):a(x){}; ~Derive(){}; void Print() {
cout<<"Hello "<<a<<endl; }
protected:
private: int a; };
void main() {
auto_ptr<CBase>pa(new Derive(5))
pa->Print(); getchar();
};
5.auto_ptr的一些常用函数:
1)auto_ptr::auto_ptr() throw()
default构造函数、生成一个不拥有任何对象的auto_ptr、将auto_ptr的值初始化为0
2)explicit auto_ptr::auto_ptr(T* ptr) throw()
生成一个auto_ptr,并拥有ptr所指对象、*this成为ptr对象的唯一拥有者,不允许有其他拥有者
3)T* auto_ptr::get() const throw()
返回auto_ptr所指对象的地址,如果auto_ptr未指向任何对象,返回null指针。
4)T& auto_ptr::operator * () const throw()
提领(deferences)操作符,返回auto_ptr所拥有的对象。如果auto_ptr并未拥有任何对象,此调用导致未定义行为(可能导致崩溃).
5)T* auto_ptr::release() throw()
放弃auto_ptr原先所拥有的对象的拥有权、返回auto_ptr原先拥有对象(如果有的话)的地址,没有则返回NULL指针
6)void auto_ptr::reset(T* ptr =0) throw()
以ptr重新初始化auto_ptr,如果auto_ptr原本拥有对象,先把它删除。