C++智能指针之auto_ptr

版权声明:guojawee https://blog.csdn.net/weixin_36750623/article/details/84820220

头文件<memory>

总结为一句话:auto_ptr是独占指针,它的出现是能够自动析构动态分配的内存,避免内存泄漏,但是auto_ptr有很多弊端,下面会通过示例和讲解一一将弊端和用法展现出来。


  1. auto_ptr不能初始化为指向非动态内存(原因很简单,delete 表达式会被应用在不是动态分配的指针上这将导致未定义的程序行为)。对象通过初始化只能指向由new创建的动态内存,它是这块内存的拥有者。

构造auto_ptr对象示例代码

// 直接构造智能指针
auto_ptr<int> p(new int(1));//推荐

// 将已存在的指向动态内存的普通指针作为参数来构造
int* np = new int(1);
auto_ptr<int> p(np);
  1. auto_ptr的用途:管理对象的生命周期,不造成内存泄漏。即:当auto_ptr对象生命周期结束时,其析构函数会将auto_ptr对象拥有的动态内存自动释放。即使发生异常,通过异常的栈展开过程也能将动态内存释放。
  2. auto_ptr不能用来管理数组指针:auto_ptr的内部实现中,析构函数中删除对象使用delete而不是delete[]。
    代码解释:使用auto_ptr的方式,在ap析构时,执行delete,仅仅释放了数组的第一个元素的空间,仍然会造成内存泄漏,所有使用auto_ptr管理数组不合理的。
int *p = new int[100];
auto_ptr<int> ap(p);
  1. 由于auto_ptr对象析构时会删除它所拥有的指针,所以使用时避免多个auto_ptr对象管理同一个指针。程序员在写代码时,很容易将智能指针指向同一块内存,并且该问题还不好察觉,因此引出下文的week_ptr禁止同一个week_ptr指针指向同一块内存(即:两个week_ptr指针指向同一块内存时,编译报错)

代码解释:这样使用会造成p1和p2在析构时都试图删除np,C++标准中多次删除同一个对象会导致未定义的行为。并且当p1析构而p2仍然被使用时,会导致空指针访问风险。

int* np = new int(1);
auto_ptr<int> p1(np);
auto_ptr<int> p2(np);
  1. C++中对一个空指针NULL执行delete操作是安全的。所以在auto_ptr的析构函数中无须判断它所拥有指针是否为空。
  2. auto_ptr智能指针的拷贝、赋值,会发生所有权的转移,详解见下:因为一块动态内存智能由一个智能指针独享,所以在拷贝构造或赋值时都会发生拥有权转移的过程。p1将失去对字符串内存的所有权,而p2将其获得。对象销毁时,p2负责内存的自动销毁

代码解释:p1失去所有权变为空,p2获得p1的所有权

auto_ptr< string > p1( new string( "Brontosaurus" ) );
auto_ptr< string > p2(p1);  //利用p1来构造p2,发生拷贝构造
auto_ptr< int > p1( new int( 1024 ) );
auto_ptr< int > p2( new int( 2048 ) );
p1 = p2;  //赋值运算
  1. 不要将auto_ptr作为函数参数按值传递。因为在函数调用过程中在函数的作用域中会产生一个局部的临时auto_ptr对象来接收传入的 auto_ptr(拷贝构造),这样,传入的实参auto_ptr的对其指针的所有权转移到了临时auto_ptr对象上,临时auto_ptr在函数退出时析构,所以当函数调用结束,原实参所指向的对象已经被删除了。
void func(auto_ptr<int> ap)
{
cout << *ap << endl;
}

auto_ptr<int> ap(new int(1));
func(ap); //值传递后,ap的所有权转移,ap失效,将不再拥有对象
cout << *ap << endl;//错误,函数调用结束后,ap已经不再拥有任何对象了
  1. 非要将auto_ptr作为参数传递时,一定要使用const &类型。
    解释:按引用传递在调用函数是不会发生所有权转移,但是无法预测函数体内的操作,有可能在函数体内进行了所有权的转移,因此按引用传递auto_ptr作为函数参数也是不安全的。使用const 引用传递则可以阻止在函数体内对auto_ptr对象的所有权转移。如果不得不使用auto_ptr对象作为函数参数时,尽量使用const引用传递参数。
  2. auto_ptr支持所拥有的指针类型之间的隐式类型转换。
    代码解释:下列代码就可以通过,实现从auto_ptr到auto_ptr的隐式转换,因为derived*可以转换成base*类型
class base{};
class derived: public base{};

auto_ptr<base> apbase = auto_ptr<derived>(new derived);
  1. 重要:auto_ptr对象不能作为STL容器元素

auto_ptr常用的成员函数

1、 get()
返回auto_ptr指向的那个对象的内存地址。如下例:

int* p = new int(33);
cout << "the adress of p:"<< p<< endl;    // 00481E00
auto_ptr<int> ap1(p);
cout << "the adress of ap1: "<< &ap1<< endl; //0012FF68
cout << "the adress of the objectwhich ap1 point to: " << ap1.get()<< endl; // 00481E00

输出结果分析:第一行与第三行相同,都是new int(33)所在的那块内存的地址。第二行是ap1是这个类对象本身所在内存的地址&ap1。

2、reset()
重新设置auto_ptr指向的对象。类似于赋值操作,但赋值操作不允许将一个普通指针指直接赋给auto_ptr,而reset()允许。

代码解释:剥夺pstr_auto拥有"Brontosaurus"字符内存的所有权,这块内存首先会被释放,之后pstr_auto再拥有"Long-neck"字符内存的所有权。

auto_ptr< string > pstr_auto( new string( "Brontosaurus" ) );
pstr_auto.reset( new string( "Long-neck" ) ); //reset

pstr_auto.reset(0); //可以释放pstr_auto对象,销毁内存。

3、release()
功能:p1.release();表示返回p1指向的那个对象的内存地址,并释放对p1对该这块内存的所有权。 ⇒ ⇒ ⇒ 用此函数初始化auto_ptr时可以避免两个auto_ptr对象拥有同一个对象的情况(与get函数相比)。

例子如下:

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

//pstr_auto、pstr_auto2都具有所有权,要避免这种情况
auto_ptr< string > pstr_auto2(pstr_auto.get() ); 

//release可以首先释放对对象的所有权,再返回该对象,此时只有pstr_auto2具有所有权
auto_ptr< string > pstr_auto2(pstr_auto.release() ); 

猜你喜欢

转载自blog.csdn.net/weixin_36750623/article/details/84820220