auto_ptr的相关使用

本文对比了auto_ptr与unique_ptr的特性与应用场景。详细解析了auto_ptr存在的内存安全问题及unique_ptr如何解决这些问题,并介绍了unique_ptr提供的额外功能。

auto_ptr简介

auto_ptr是STL中智能指针的一员,与C++98引入,定义在头文件**< memory >**中。
正如前一篇所说,其功能和用法类似于unique_ptr,由new expression获得对象;
当aut_ptr对象销毁时,它说管理的对象也会自动被delete掉。

auto_ptr被unique_ptr替换的原因

(1)出于安全考虑

对于赋值语句:

auto_ptr< string> ps (new string ("I reigned lonely as a cloud.”);
auto_ptr<string> vocation; 
vocaticn = ps;

对于以上的赋值语句,如果ps和vocation是常规指针,则:

  • 这两个指针将会指向同一个string对象。

而出现了这种情况,就会造成错误,因为程序将会试图删除一个对象两次,一次在ps过期时,另一次则是在vocation过期时。
想要避免这样的情况发生,可取的方法是:
(1)定义赋值运算符,使之执行深复制。这样两个指针指向的就是两个不同的对象,其中的一个对象是另外一个对象的副本。虽然可行,但是会浪费空间,因此,智能指针并没有采取这种方案。
(2)建立 所有权(ownership) 的概念。对于特定的对象,只能有一个只能指针可拥有,这样只有拥有对象的智能指针的析构函数会删除对象。然后,让赋值操作转让这样的所有权。这是用于auto_ptr和unique_ptr的策略,但是unique_ptr的策略更严格。
(3)创建智能更高的指针,跟踪引用特定对象的智能指针数。这称为引用计数。例如,赋值时,计数将加1,而指针过期时,计数将减1。当减为0时才调用delete。这是shared_ptr采用的策略。

同样的策略也适用于复制构造函数,即auto_ptr vocation(ps)时也需要上面的策略。


#include <iostream>
#include <string>
#include <memory>
using namespace std;

int main()
{
	auto_ptr<string> films[5] ={
	auto_ptr<string> (new string("Fowl Balls")),
	auto_ptr<string> (new string("Duck Walks")),
	auto_ptr<string> (new string("Chicken Runs")),
	auto_ptr<string> (new string("Turkey Errors")),
	auto_ptr<string> (new string("Goose Eggs"))
	};
    auto_ptr<string> pwin;

	// films[2] loses ownership! 将所有权从films[2]转让给pwin,此时films[2]不再引用该字符串从而变成空指针
    pwin = films[2]; 

	cout << "The nominees for best avian baseballl film are ";
	for(int i = 0; i < 5; ++i)
	{
		cout << *films[i] << endl;
	}
 	cout << "The winner is " << *pwin << endl;
	return 0;
}

在上面的程序中,会发生崩溃,原因如上所说,films[2]已经是空指针了,下面输出访问空指针自然会发生崩溃。

然而,如果将auto_ptr替换为shared_ptr或者unique_ptr后,程序就不会发生崩溃了,具体情况如下:

  • 当使用shared_ptr时,运行时正常的,因为shared_ptr采用引用计数,因此pwin和films[2]所指的都是同一块内存,在释放空间时,因为事先要判断引用计数值的大小,于是不会出现多次删除一个对象的错误。
  • 当使用unique_ptr时,会发生编译错误,和quto_ptr类似,unique_ptr也采用所有权模型,但是不同之处在于:程序不会等到运行阶段崩溃,而在编译期因下述代码出现错误:
unique_ptr<string> pwin;
pwin = films[2];

指导你发现潜在的内存错误。这就是为何要摒弃auto_ptr的原因,一句话总结就是:避免因潜在的内存问题导致程序崩溃

由上面可见,unique_ptr比auto_ptr更安全:

  • 因为auto_ptr有拷贝语义,拷贝之后原对象会变得无效,再次访问原对象会导致程序崩溃;
  • 而unique_ptr则禁止了拷贝语义,但是提供了移动语义,即可以使用std::move()进行控制权限的转移
unique_ptr<string> upt(new string("lvlv"));
unique_ptr<string> upt1(upt);	//编译出错!已禁止拷贝
unique_ptr<string> upt1=upt;	//编译出错!已禁止拷贝
unique_ptr<string> upt1=std::move(upt);  //控制权限转移

auto_ptr<string> apt(new string("lvlv"));
auto_ptr<string> apt1(apt);	//编译通过
auto_ptr<string> apt1=apt;	//编译通过

在上面的代码中,使用std::move将unique_ptr的控制权限转移后,就不能在通过unique_ptr来访问和控制资源了,否则同样会出现程序的崩溃。

在使用unique_ptr访问资源之前,可以使用成员函数get()进行判空操作。

unique_ptr<string> upt1=std::move(upt);  		//控制权限转移
if(upt.get()!=nullptr)					//判空操作更安全
{
	//do something
}
(2)unique_ptr不仅安全,而且灵活

如果unique_ptr是个临时右值,编译器允许拷贝语义。

unique_ptr<string> demo(const char * s)
{
    unique_ptr<string> temp (new string (s))return temp;
}

//假设编写了如下代码:
unique_ptr<string> ps;
ps = demo('Uniquely special")

在上面的代码中,demo()返回一个临时unique_ptr,然后ps接管了临时变量unique_ptr所管理的资源,而返回时时临时的unique_ptr会被销毁,也就是说没有机会使用unique_ptr来访问无效的数据
换句话来说,这种赋值是不会出现任何问题的,即没有理由禁止这种赋值。实际上,编译器确实允许这种赋值。相对于auto_ptr任何情况下都允许拷贝语义,这正是unique_ptr更加灵活聪明的地方。

(3)扩展auto_ptr不能完成的功能

(1)unique_ptr可放在容器中,弥补了auto_ptr不能作为容器元素的缺点。

//方法1
vector<unique_ptr<string>> vs { new string{“Doug”}, new string{“Adams”} };  

//方法2
vector<unique_ptr<string>>v;
unique_ptr<string> p1(new string("abc"));  

(2)管理动态数组,因为unique_ptr有unique_ptr<X[]>重载版本,销毁动态对象时调用delete[]。

unique_ptr<int[]> p (new int[3]{1,2,3});  
p[0] = 0;// 重载了operator[]

(3)自定义资源删除操作(Deleter)。unique_ptr默认的资源删除操作是delete/delete[],若需要,可以进行自定义:

void end_connection(connection *p) { disconnect(*p); } //资源清理函数

//资源清理器的“类型” 
unique_ptr<connection, decltype(end_connection)*> p(&c, end_connection);// 传入函数名,会自动转换为函数指针 

综上所述,基于unique_ptr的安全性和扩充的功能,unique_ptr成功的将auto_ptr取而代之。

参考文章:
STL四种智能指针

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值