c++智能指针用法

一、shared_ptr定义

c++新标准库提供了两种智能指针类型来管理动态对象。shared_ptr和unique_ptr。头文件memory。

shared_ptr允许多个指针指向同一个对象
unique_ptr则“独占”所指向的对象    

智能指针是模板,要在尖括号内给出类型。

shared_ptr<string> p1;
shared_ptr<list<int>> p2;

解引用一个智能指针返回它指向的对象。如果在一个条件判断中使用智能指针,效果就是检测它是否为空。

1
2
3
//如果p1不为空,检查它是否指向一个空string
if(p1 && p1->empty())
*p1 = "hi";// 如果p1指向一个空string,解引用p1,将一个新值赋予string

shared_ptr unique_ptr都支持的操作

语法 解释
shared_ptr< T > sp 空智能指针,可以指向类型为T的对象
unique_ptr< T > up
p 将p用作一个条件判断,若p指向一个对象,则为true
*p 解引用p,获得它指向的对象
p->mem 等价于(*p).mem
p.get() 返回p中保存的指针。要小心使用,若智能指针释放其对象,返回的指针指向的对象也消失了
swap(p, q) 交换p和q的指针
p.swap(q)

shared_ptr独有的操作

语法 解释
make_shared< T > (args) 返回一个shared_ptr,指向一个动态分配的类型为T的对象。使用args初始化次对象
shared_ptr< T > p(q) p是shared_ptr q的拷贝:此操作会递增q中的计数器。q中的指针必须能转换为T*
p = q p和q都是shared_ptr,所保存的指针必须能相互转换。此操作会递减p的引用计数,递增q的引用计数;若p的引用计数变为0,则将其管理的原内存释放。
p.unique 若p.use_count() 为1,返回true;否则返回false
p.use_count() 返回与p共享对象的智能指针数量;可能很慢,主要用于调试

make_shared函数
此函数在动态内存分配一个对象并初始化它,返回指向此对象的shared_ptr。头文件memory。
使用时要保持类型一致。

1
2
3
4
5
6
7
8
9
10
11
//指向一个值为42的int的shared_ptr
shared_ptr<int> p3 = make_shared<int>(42);

//p4指向一个值为"9999999999"的string
shared_ptr<string> p4 = make_shared<string>(10,'9');

//p5指向一个值初始化的int,即,值为0
shared_ptr<int> p5 = make_shared<int>();

//使用auto,p6指向一个动态分配的空vector<string>
auto p6 = make_shared<vector<string>>();

shared_ptr的拷贝和赋值
当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象。

1
2
3
4
5
6
7
8
auto p = make_shared<int> (42);	//p指向的对象只有p一个引用者
auto q(p); //p和q指向相同对象,此对象有两个引用者

auto r = make_shared<int> (42); //r指向的int只有一个引用者
r = q; //给r赋值,令它指向另一个地址
//递增q指向的对象的引用计数
//递减r原来指向的对象的引用计数
//r原来指向的对象已没有引用者,会自动释放

空悬指针(dangling pointer):指向一块曾经保存数据对象但现在已经无效(被delete掉)的内存的指针。

二、shared_ptr和new结合
可以用new返回的指针来初始化智能指针。

1
2
shared_ptr<double> p1;		
shared_ptr<int> p2(new int(42));

接受指针参数的智能指针构造函数是explicit的,不能将一个内置指针隐式转换为一个智能指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
shared_ptr<int> p1 = new int(1024);		//错误:必须使用直接初始化形式
shared_ptr<int> p2(new int(1024)); //正确:使用了直接初始化形式

shared_ptr<int> clone(int p)
{
return new int(p); //错误:隐式转换为shared_ptr<int>
}

shared_ptr<int> clone(int p)
{
//正确:显示地用int*创建shared_ptr<int>
return shared_ptr<int> (new int(p));
}

定义和改变shared_ptr的其他方法

语法 解释
shared_ptr< T > p(q) p管理内置指针q所指向的对象;q必须指向new分配的内存,且能够转换为T*类型
shared_ptr< T > p(u) p从unique_ptr u那里接管了对象的所有权;将u置为空
shared_ptr< T > p(q, d) p接管了内置指针q所指向的对象的所有权。q必须能转换为T*类型。p将使用可调用对象d来代替delete
shared_ptr< T > p(p2, d) p是shared_ptr p2的拷贝,唯一的区别是p将用可调用对象d来代替delete
p.reset() 若p是唯一指向其对象的shared_ptr,reset会释放次对象。若传递了可选的参数内置指针q,会令p指向q,否则会将p置为空。若还传递了参数d,讲会调用d而不是delete来释放q。
p.reset(q)
p.reset(p,d)

不要混合使用普通指针和智能指针。
不要使用get初始化另一个智能指针或为智能指针赋值。
其他操作。reset成员经常与unique一起使用,来控制多个shared_ptr共享的对象,在改变底层对象之前,我们检查自己是否是当前对象仅有的用户。如果不是,在改变之前要制作一份新的拷贝。

1
2
3
if(!p.unique())
p.reset(new string(*p));
*p += newVal;

使用d代替delete的例子。

1
2
3
4
5
6
7
8
9
10
struct destination;		//表示我们正在连接什么
struct connection; //使用连接所需的信息
connection connect(destination*); //打开连接
void disconnect(connection); //关闭给定的连接
void end_connection(connection *p) {disconnect(*p);} //代替delete
void f(destination &d)
{
connection c = connect(&d);
shared_ptr<connection> p(&c, end_connection);
}

unique_ptr
某个时刻只能有一个unique_ptr指向一个给定对象。当unique_ptr被销毁时,它所指向的对象也被销毁。定义一个unique_ptr时,需要将其绑定到一个new返回的指针上。

1
2
unique_ptr<double> p1;
unique_ptr<double> p2(new int(42));

unique_ptr不支持普通的拷贝或赋值。

1
2
3
4
unique_ptr<string> p1(new string("Stegosaurus"));
unique_ptr<string> p2(p1); //错误
unique_ptr<string> p3;
p3 = p2 //错误

unique_ptr操作

语法 解释
unique_ptr< T > u1 空unique_ptr,可以指向类型为T的对象。u1会使用delete来释放它的指针;u2会使用一个类型为D的可调用对象来释放它的指针
unique_ptr < T , D > u2
unique_ptr < T , D > u(d) 空unique_ptr,可以指向类型为T的对象,用类型为D的对象d代替delete
u = nullptr 释放u指向的对象,将u置为空
u.release() u放弃对指针的控制权,返回指针,并将u置为空
u.reset() 释放u指向的对象
u.reset(q) 如果提供了内置指针q,令u指向这个对象;否则将u置为空
u.reset(nullptr)
1
2
3
unique_ptr<string> p2(p1.release());	//将所有权从p1(指向String Stegosaurus)转移给p2,release将p1置为空
unique_ptr<string> p3(new string("Trex"));
p2.reset(p3.release());将所有权从p3转移给p2,reset释放了p2原来指向的内存

可以拷贝或赋值一个将要被销毁的unique_ptr。如下。

1
2
3
4
unique_ptr<int> clone(int p)
{
return unique_ptr<int>(new int(p));
}

向unique_ptr传递删除器,以shared_ptr中connection为例。

1
2
3
4
5
void f(destination &d)
{
connection c = connect(&d);
unique_ptr<connection, decltype(end_connection)*> p(&c, end_connection);
}

三、weak_ptr
weak_ptr是一个 不控制所指对象生存期的智能指针,它指向由一个shared_ptr管理的对象。不会改变计数。一旦最后一个指向对象的shared_ptr被销毁,即使有weak_ptr指向对象,对象也会被释放。、

weak_ptr用法

语法 解释
weak_ptr< T > w 空weak_ptr可以指向类型为T的对象
weak_ptr< T > w(sp) 与shared_ptr sp指向相同对象的weak_ptr。T必须能转换为sp指向的类型
w = p p可以是一个shared_ptr或一个weak_ptr。赋值后w与p共享对象
w.reset() 将w置为空
w.use_count() 与w共享对象的shared_ptr的数量
w.expired() 若w.use_count()为0,返回true,否则返回false
w.lock() 如果expired为true,返回一个空shared_ptr;否则返回一个指向w的对象的shared_ptr
1
2
3
4
5
6
7
auto p = make_shared<int>(42);
weak_ptr<int> wp(p); //wp弱共享p;p的引用计数未改变

if(shared_ptr<int> np = wp.lock()) //调用lock检查weak_ptr所指向的对象是否存在
{
//在if中,np与p共享对象
}