智能指针

智能指针是行为类似于指针的类对象。模板auto_ptr是C++98提供的解决方案,C++11已将其摒弃,并提供了另外两种解决方案,unique_ptr和shared_ptr。

1. 使用智能指针

这三个智能指针模板(auto_ptr、unique_ptr和shared_ptr)都定义了类似指针的对象,可以将new获得(直接或间接)的地址赋给这种对象。当智能指针过期时,其析构函数将使用delete来释放内存。常规指针与auto_ptr的区别,如下图所示:
在这里插入图片描述

//创建int类型的智能指针
#include <memory>
std::auto_ptr<int> pi(new int);//替代int* p = new int;
std::unique_ptr<int> piu(new int);
std::shared_ptr<int> pis(new int);

double *p = new double;
std::shared_ptr<double> pb(p);
std::shared_ptr<double> pb2 =shared_ptr<double>(p); 
std::shared_ptr<double> pb3 = p; //x 隐式转换,不允许

所有智能指针类都一个explicit构造函数,该构造函数将指针作为参数。因此不需要自动将指针转换为智能指针对象。

对全部三种智能指针都应避免的一点:
在这里插入图片描述pvac过期时,程序将把delete运算符用于非堆内存,这是错误的。

智能指针对象的很多方面都类似于常规指针。例如,如果ps是一个智能指针对象,则可以对它执行解除引用操作(* ps)、用它来访问结构成员(ps->puffIndex)、将它赋给指向相同类型的常规指针。还可以将智能指针对象赋给另一个同类型的智能指针对象,但将引起一个问题。

2.有关智能指针的注意事项

为何C++11摒弃auto_ptr呢?先来看下面的赋值语句:
在这里插入图片描述
如果ps和vocation是常规指针,则两个指针将指向同一个string对象。这是不能接受的,因为程序将试图删除同一个对象两次——一次是ps过期时,另一次是vocation过期时。要避免这种问题,方法有多种:

  • 定义赋值运算符,使之执行深复制。这样两个指针将指向不同的对象,其中的一个对象是另一个对象的副本。
  • 建立所有权(ownership)概念,对于特定的对象,只能有一个智能指针可拥有它,这样只有拥有对象的智能指针的构造函数会删除该对象。然后,让赋值操作转让所有权。这就是用于auto_ptr和unique_ptr的策略,但unique_ptr的策略更严格。
  • 创建智能更高的指针,跟踪引用特定对象的智能指针数。这称为引用计数(reference counting)。例如,赋值时,计数将加1,而指针过期时,计数将减1。仅当最后一个指针过期时,才调用delete。这是shared_ptr采用的策略。

下例是不适合使用auto_ptr的示例。

#include<iostream>
#include<string>
#include<memory>
int main()
{
	using namespace std;
	auto_ptr<string> films[5] =
	{
		auto_ptr<string> (new string("Fowl Balls")),
		auto_ptr<string> (new string("Duck Walk")),
		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;
	pwin = films[2];//此时films[2]失去了所有权
	
	for(int i =0;i<5;i++)
		cout<<*films[i]<<endl;
	return 0;
}

程序输出如下:
在这里插入图片描述
这里的问题在于,下面的语句将所有权从films[2]转让给pwin:

pwin = films[2];

这导致films[2]不再引用该字符串。在auto_ptr放弃对象的所有权后,便可能使用它来访问该对象。当程序打印films[2]指向的字符串时,却发现这是一个空指针。使用shared_ptr代替auto_ptr(这要求编译器支持C++11新增的shared_ptr类),则程序将正常运行。若使用unique_ptr时,程序不会等到运行阶段崩溃,而在编译器因下述代码行出现错误:

pwin = films[2];

因此,unique_ptr比auto_ptr更安全(编译阶段错误比潜在的程序崩溃更安全)。

有时候,将一个智能指针赋给另一个并不会留下危险的悬挂指针。程序试图将一个unique_ptr赋给另一个时,如果源unique_ptr是个临时右值,编译器允许这样做;如果源unique_ptr将存在一段时间,编译器将禁止这样做:
在这里插入图片描述
语句#1将留下悬挂的unique_ptr(pul),这可能导致危害。语句#2不会留下悬挂的unique_ptr,因为它调用unique_ptr的构造函数,该构造函数创建的临时对象在其所有权转让给pu后就会被销毁。

要安全地重用这种指针,可给它赋新值。C++有一个标准库函数std::move( ),让您能够将一个unique_ptr赋给另一个。
在这里插入图片描述
模板auto_ptr使用delete而不是delete [ ],因此只能与new一起使用,而不能与new [ ]一起使用。但unique_ptr有使用new [ ]和delete [ ]的版本:
在这里插入图片描述
注:
使用new分配内存时,才能使用auto_ptr和shared_ptr,使用new [ ]分配内存时,不能使用它们。不使用new分配内存时,不能使用auto_ptr或shared_ptr;不使用new或new []分配内存时,不能使用unique_ptr。

在这里插入图片描述

3.选择智能指针

如果程序要使用多个指向同一个对象的指针,应选择shared_ptr。包括:有一个指针数组,并使用一些辅助指针来标识特定的元素,如最大的元素和最小的元素;两个对象包含都指向第三个对象的指针;STL容器包含指针。很多STL算法都支持复制和赋值操作,这些操作可用于shared_ptr,但不能用于unique_ptr(编译器发出警告)和auto_ptr(行为不确定)。

如果程序不需要多个指向同一个对象的指针,则可使用unique_ptr

在unique_ptr为右值时,可将其赋给shared_ptr。
在这里插入图片描述
在满足unique_ptr要求的条件时,也可使用auto_ptr,但unique_ptr是更好的选择。

4.补充weak_ptr

weak_ptr可以指向shared_ptr指针指向的对象内存,却并不用用该内存。使用weak_ptr成员lock可以返回其指向内存的shared_ptr对象,且在所指对象内存已经无效时返回nullptr。

#include<iostream>
#include <memory>
using namespace std;
//enum class type{a,b};

void CheckWp(weak_ptr<int> &wp)
{
	shared_ptr<int> sp = wp.lock();//转换为shared_ptr
	if(sp == nullptr) 
		cout<<"point is invalid"<<endl;
	else
		cout<<"still "<<*sp<<endl;
	return;
}

int main()
{
	shared_ptr<int> sp1 = make_shared<int>(22);
	shared_ptr<int> sp2 = sp1;
	weak_ptr<int> wp = sp1;//指向shared_ptr<int> 所指对象
	cout<<*sp1<<endl;//22
	cout<<*sp2<<endl;//22	
	CheckWp(wp);//still 22
	sp1.reset();
	cout<<*sp2<<endl;//22	
	CheckWp(wp);//still 22
	sp2.reset();
	CheckWp(wp);//point is invalid
	return 0;
 } 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值