C++中的智能指针(auto_ptr)

时间:2023-12-23 19:54:31

实际上auto_ptr 仅仅是C++标准库提供的一个类模板,它与传统的new/delete控制内存相比有一定优势。使用它不必每次都手动调用delete去释放内存。当然有利也有弊,也不是全然完美的。

本文从以下的8个方面来总结auto_ptr使用的大部分内容。

1. auto_ptr是什么?

auto_ptr 是C++标准库提供的类模板,auto_ptr对象通过初始化指向由new创建的动态内存,它是这块内存的拥有者,一块内存不能同一时候被分给两个这样拥有者(auto_ptr)。

当auto_ptr对象生命周期结束时。其析构函数会将auto_ptr对象拥有的动态内存自己主动释放。即使发生异常,通过异常的栈展开过程也能将动态内存释放。auto_ptr不支持new 数组。

2. auto_ptr须要包括的头文件?

#include <memory>

3. 初始化auto_ptr对象的方法?

1) 构造函数

1] 将已存在的指向动态内存的普通指针作为參数来构造

int* p = new int(33);

auto_ptr<int> api(p);

这样在api对象的生命周期结束的时候。会调用自身的析构函数,并delete来释放拥有的内存,这样我们就不须要写以下的代码:

delete p;

2] 直接构造智能指针

auto_ptr< int > api( new int( 33 ) );

2) 拷贝构造

利用已经存在的智能指针来构造新的智能指针

auto_ptr< string > pstr_auto( new string( "Brontosaurus" ) );

auto_ptr< string > pstr_auto2(pstr_auto );  //利用pstr_auto来构造pstr_auto2

由于auto_ptr智能指针仅仅能独享一块动态内存,所以在拷贝构造或赋值时都会发生拥有权的转移。

在此拷贝构造过程中,pstr_auto将失去对字符串内存的全部权,而pstr_auto2将其获得。

pstr_auto2负责内存的自己主动销毁。

3) 赋值

利用已经存在的智能指针来构造新的智能指针

auto_ptr< int > p1( new int( 1024 ));

auto_ptr< int > p2( new int( 2048 ));

p1 = p2;

在赋值之前。由p1 指向的对象被删除。赋值之后。p1 拥有int 型对象的全部权。该对象值为2048。 p2 不再被用来指向该对象。

4. 空的auto_ptr 须要初始化吗?

通常的指针在定义的时候若不指向不论什么对象,我们用Null给其赋值。

对于智能指针,由于构造函数有默认值0,我们能够直接定义空的auto_ptr例如以下:

auto_ptr< int >p_auto_int;  //不指向不论什么对象

5. 防止两个auto_ptr对象拥有同一个对象(一块内存)

由于auto_ptr的全部权独有。不能共享动态分配的内存。所以以下的代码会造成混乱。

int* p = new int(0);

auto_ptr<int> ap1(p);

auto_ptr<int> ap2(p);

由于ap1与ap2都觉得指针p是归它管的,在析构时都试图删除p, 两次删除同一个对象的行为在C++标准中是没有定义的。所以我们必须防止这样使用auto_ptr。

6. 警惕智能指针作为參数!

1) 按值传递时,函数调用过程中在函数的作用域中会产生一个局部对象来接收传入的auto_ptr(拷贝构造)。这样。传入的实參auto_ptr就失去了其对原对象的全部权,而该对象会在函数退出时被局部auto_ptr删除。例如以下例:

void f(auto_ptr<int> ap)

{

cout<<*ap;

}

auto_ptr<int> ap1(new int(0));

f(ap1);

cout<<*ap1; //错误,经过f(ap1)函数调用,ap1已经不再拥有不论什么对象了。

2) 出入的是引用或指针时,不会存在上面的拷贝过程。

但我们并不知道在函数中对传入的auto_ptr做了什么。假设其中某些操作使其失去了对对象的全部权。那么这还是可能会导致致命的运行期错误。

结论:const reference是智能指针作为參数传递的底线。

7. auto_ptr不能初始化为指向非动态内存

原因非常easy。delete 表达式会被应用在不是动态分配的指针上这将导致没有定义的程序行为。

8. auto_ptr经常使用的成员函数

1) get()

返回auto_ptr指向的那个对象的内存地址。例如以下例:

int* p = new int(33);

cout << "the adress of p:"<< p << endl;

auto_ptr<int> ap1(p);

cout << "the adress of ap1:" << &ap1 << endl;

cout << "the adress of theobject which ap1 point to: " << ap1.get() << endl;

输出例如以下:

the adress of p: 00481E00

the adress of ap1: 0012FF68

the adress of the object which ap1point to: 00481E00

第一行与第三行同样,都是int所在的那块内存的地址。

第二行是ap1这个类对象本身所在内存的地址。

2) reset()

又一次设置auto_ptr指向的对象。

类似于赋值操作,但赋值操作不同意将一个普通指针指直接赋给auto_ptr,而reset()同意。例如以下例:

auto_ptr< string > pstr_auto( new string( "Brontosaurus" ) );

pstr_auto.reset( new string("Long -neck" ) );

在样例中。重置前pstr_auto拥有"Brontosaurus"字符内存的全部权,这块内存首先会被释放。之后pstr_auto再拥有"Long -neck"字符内存的全部权。

注:reset(0)能够释放对象。销毁内存。

3) release()

返回auto_ptr指向的那个对象的内存地址,并释放对这个对象的全部权。

用此函数初始化auto_ptr时能够避免两个auto_ptr对象拥有同一个对象的情况(与get函数相比)。

样例例如以下:

auto_ptr< string > pstr_auto( new string( "Brontosaurus" ) );

auto_ptr< string > pstr_auto2(pstr_auto.get() ); //这是两个auto_ptr拥有同一个对象

auto_ptr< string > pstr_auto2(pstr_auto.release() ); //release能够首先释放全部权