概述:C++中有很多种智能指针,auto_ptr就是其中的一种,该智能指针主要是为了解决“因程序异常退出发生的内存泄漏”这类问题的。
我们先来看下面的问题代码
#include<iostream>
#include<memory>
#include<exception>
using namespace std;
//一般指针的处理方式
template<typename T>
class OldClass
{
public:
OldClass(T a)
{
ptr = new T(a);
}
~OldClass()
{
delete ptr;
}
private:
T * ptr;
};
int main()
{
OldClass<int> a(2);
int * p = new int(1);
throw runtime_error("1");//程序发生异常退出
delete p;
}
在上面的例子中类OldClass中在构造函数中为成员指针开辟内存资源,在析构函数中释放内存资源;现在如果在main主函数中发生异常即throw runtime_error("1");导致程序退出;则该程序将会造成严重影响,即:一、main函数中的指针p未被释放;二、由于程序的异常结束将类对象a的析构函数也将不会被调用,即对象a自身的内存资源也没有被释放。这两种情况都将会造成内存泄漏。
常规的解决办法就是利用异常处理来解决,具体修改代码如下:
int main()
{
OldClass<int> a(2);
try
{
int * p = new int(1);
//发生异常退出
try
{
throw runtime_error("1");
}
catch(exception ex)
{
delete p;//必须针对每一个可能因异常而未释放资源的指针先进行释放处理
throw ex;//再继续将原异常抛出
}
delete p;
}
catch(runtime_error ex)
{
cerr<<ex.what()<<endl;
//此处进行异常处理
}
}
即必须针对每一个可能因为异常而无法释放的指针在异常传递的中间先释放该指针后再继续原异常抛出,可见这种办法很为复杂,因为必须为每一个指针都进行类似的处理。
智能指针的出现:为了方便解决该问题的首段之一就是智能指针,即可以将任何一种类型(包括自定义类型)转换成自能指针,转换成智能指针后由于智能指针本身就是区域变量,所以无论程序是正常退出还是异常退出,只要程序退出时则智能指针都会被自动销毁释放内存资源。
下面对智能指针的常见用法和注意点做几点详细介绍:
一、智能指针的初始化时,只能用一个智能指针给另一个智能指针初始化,普通指针则不能。
<span style="white-space:pre"> </span>auto_ptr<int> a = auto_ptr<int>(new int(2));
int *p = new int(1);
//auto_ptr<int> b = p;//报错
auto_ptr<int> c(p);
cout<<*a<<endl;
cout<<*c<<endl;
/*
输出结果:
2
<span style="white-space:pre"> </span>1
<span style="white-space:pre"> </span>请按任意键继续. . .
*/
二、每一个智能指针都是它所指向的对象的拥有者,且每一个对象都只能有一个拥有者,所以智能指针的构造函数和赋值操作原理上都是拥有权ownership的转移
auto_ptr<int> a = auto_ptr<int>(new int(2));
auto_ptr<int> c = auto_ptr<int>(new int(1));
//a将值为2的对象的拥有权转移给c,a变成一个null指针
//c将值为1的对象销毁并交出拥有权后接受a转移的拥有权
c = a;
//c将拥有权转移给d,自己变成null指针
auto_ptr<int> d(c);
三、智能指针和一般指针的用法一致,都含有operate*和operate->操作,但是智能指针却不能进行任何算术运算,这是由于智能指针拥有权的特性决定的。
四、智能指针做为函数的参数类型或返回值类型时都会发生对象拥有权的转移,如果将函数外部对象的拥有权转移进入函数内,而不再用返回值将其拥有权转移出来的话,则该外部对象将会随着函数调用的结束会被销毁,这是很可怕的事情所以要慎用。
五、在函数间传递智能指针时最安全的方式是用常量引用constant reference,这样外部对象的拥有权不会被转移到函数内部的局部变量中,当然这样的对象转递进去后是不能修改的,不能赋值给别的对象或被别的对象给赋值;也你建议用引用reference去传递智能指针对象,因为这样很容易造成拥有权的混淆。
auto_ptr<int> a = auto_ptr<int>(new int(2));
auto_ptr<int> c = auto_ptr<int>(new int(1));
const auto_ptr<int> & d = c;
cout<<*a<<endl;
cout<<*c<<endl;
cout<<*d<<endl;
//输出2、1、1
//a = d;编译报错
值得加以说明的是:对于auto_ptr对象的constant reference与一般的对象的常量引用有所不同,对于auto_ptr智能指针对象const并非意味着你不能更改该指针所拥有的对象,而是意味着你不能更改auto_ptr的拥有权。
六、不村子针对数组array的智能指针,因为auto_ptr是透过delete操作而非delete[]操作来销毁对象的。
七、智能指针对象不能做为标准容器的元素,因为标准容器中最基本的复制和拷贝操作都会使原有的auto_ptr对象交出拥有权,而不是拷贝给新的对象。
下面附上智能指针运用完整例子:
#include<iostream>
#include<memory>
#include<exception>
using namespace std;
//智能指针处理
template<typename T>
class NewClass
{
public:
NewClass(T a)
{
ptr = auto_ptr<T>(new T(a));
}
NewClass(const NewClass<T> &nc):ptr(new T(nc.Get())){}
~NewClass(){}
T Get(){return *ptr;}
void Set(T t){*ptr = t;}
private:
auto_ptr<T> ptr;
};
template<typename T>
void fun(const auto_ptr<T> & ap)
{
//该函数的功能是将指针所指对象中的数据成员加陪
ap->Set((*ap).Get()*2);
}
int main()
{
//建立NewClass对象的智能指针
auto_ptr<NewClass<int>> a(new NewClass<int>(2));
cout<<a->Get()<<endl;
//将a中的数据加倍
fun(a);
cout<<a->Get()<<endl;
/*
2
4
请按任意键继续. . .
*/
}
最后简单小结一下:其实aoto_ptr智能指针确实还是挺方便的,只是自己在运用的时候多注意对象拥有者的唯一性,不要轻易的进行指针间的赋值或传值就可以了。