C++中的智能指针首先出现在“准”标准库boost中。随着使用的人越来越多,为了让开发人员更方便、更安全的使用动态内存,C++11也引入了智能指针来管理动态对象。在新标准中,主要提供了shared_ptr、unique_ptr、weak_ptr三种不同类型的智能指针。接下来的几篇文章,我们就来总结一下这些智能指针的使用。

今天,我们先来看看shared_ptr智能指针。

shared_ptr
shared_ptr是一个引用计数智能指针,用于共享对象的所有权也就是说它允许多个指针指向同一个对象。这一点与原始指针一致。

先来一段简单的代码,看看shared_ptr的简单使用:

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

class Example
{
public:
Example() : e(1) { cout << "Example Constructor..." << endl; }
~Example() { cout << "Example Destructor..." << endl; }

int e;
};

int main() {

shared_ptr<Example> pInt(new Example());
cout << (*pInt).e << endl;
cout << "pInt引用计数: " << pInt.use_count() << endl;

shared_ptr<Example> pInt2 = pInt;
cout << "pInt引用计数: " << pInt.use_count() << endl;
cout << "pInt2引用计数: " << pInt2.use_count() << endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
程序输出如下:

Example Constructor...
pInt: 1
pInt引用计数: 1
pInt引用计数: 2
pInt2引用计数: 2
Example Destructor...
1
2
3
4
5
6
从上面这段代码中,我们对shared_ptr指针有了一些直观的了解。一方面,跟STL中大多数容器类型一样,shared_ptr也是模板类,因此在创建shared_ptr时需要指定其指向的类型。另一方面,正如其名一样,shared_ptr指针允许让多个该类型的指针共享同一堆分配对象。同时shared_ptr使用经典的“引用计数”方法来管理对象资源,每个shared_ptr对象关联一个共享的引用计数。对于“引用计数”的问题,这里就不展开介绍。有兴趣的同学可以上网查找资料,或者看看我的另一篇文章 【Cocos2d-x源码分析】 Cocos2d-x内存管理解析(Cocos2d-x也使用了“引用计数”方法来实现内存管理)。

对于shared_ptr在拷贝和赋值时的行为,《C++Primer第五版》中有详细的描述:

每个shared_ptr都有一个关联的计数值,通常称为引用计数。无论何时我们拷贝一个shared_ptr,计数器都会递增。
例如,当用一个shared_ptr初始化另一个shred_ptr,或将它当做参数传递给一个函数以及作为函数的返回值时,它
所关联的计数器就会递增。当我们给shared_ptr赋予一个新值或是shared_ptr被销毁(例如一个局部的
shared_ptr离开其作用域)时,计数器就会递减。一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理
的对象。
1
2
3
4
5
对比我们上面的代码可以看到:当我们将一个指向Example对象的指针交给pInt管理后,其关联的引用计数为1。接下来,我们用pInt初始化pInt2,两者关联的引用计数值增加为2。随后,函数结束,pInt和PInt2相继离开函数作用于,相应的引用计数值分别自减1最后变为0,于是Example对象被自动释放(调用其析构函数)。

接下来,我们完整地介绍一下shared_ptr的常见用法:

1、创建shared_ptr实例
最安全和高效的方法是调用make_shared库函数,该函数会在堆中分配一个对象并初始化,最后返回指向此对象的share_ptr实例。如果你不想使用make_ptr,也可以先明确new出一个对象,然后把其原始指针传递给share_ptr的构造函数。

示例如下:

int main() {

// 传递给make_shared函数的参数必须和shared_ptr所指向类型的某个构造函数相匹配
shared_ptr<string> pStr = make_shared<string>(10, 'a');
cout << *pStr << endl; // aaaaaaaaaa

int *p = new int(5);
shared_ptr<int> pInt(p);
cout << *pInt << endl; // 5
}
1
2
3
4
5
6
7
8
9
10
2、访问所指对象
shared_ptr的使用方式与普通指针的使用方式类似,既可以使用解引用操作符*获得原始对象进而访问其各个成员,也可以使用指针访问符->来访问原始对象的各个成员。

3、拷贝和赋值操作
我们可以用一个shared_ptr对象来初始化另一个share_ptr实例,该操作会增加其引用计数值。

例如:

int main() {
shared_ptr<string> pStr = make_shared<string>(10, 'a');
cout << pStr.use_count() << endl; // 1

shared_ptr<string> pStr2(pStr);
cout << pStr.use_count() << endl; // 2
cout << pStr2.use_count() << endl; // 2
}
1
2
3
4
5
6
7
8
如果shared_ptr实例p和另一个shared_ptr实例q所指向的类型相同或者可以相互转换,我们还可以进行诸如p = q这样赋值操作。该操作会递减p的引用计数值,递增q的引用计数值。

例如:

class Example
{
public:
Example(string n) : name(n) { cout << n << " constructor..." << endl; }
~Example() { cout << name << " destructor..." << endl; }

string name;
};

int main() {

shared_ptr<Example> pStr = make_shared<Example>("a object");
shared_ptr<Example> pStr2 = make_shared<Example>("b object");
cout << pStr.use_count() << endl;
cout << pStr2.use_count() << endl;

pStr = pStr2; // 此后pStr和pStr指向相同对象
cout << pStr->name << endl;
cout << pStr2->name << endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
输出如下:

a object constructor...
b object constructor...
1
1
a object destructor...
b object
b object
b object destructor...
1
2
3
4
5
6
7
8
4、检查引用计数
shared_ptr提供了两个函数来检查其共享的引用计数值,分别是unique()和use_count()。

在前面,我们已经多次使用过use_count()函数,该函数返回当前指针的引用计数值。值得注意的是use_count()函数可能效率很低,应该只把它用于测试或调试。

unique()函数用来测试该shared_ptr是否是原始指针唯一拥有者,也就是use_count()的返回值为1时返回true,否则返回false。

示例:

int main() {
shared_ptr<string> pStr = make_shared<string>(10, 'a');
cout << pStr.unique() << endl; // true

shared_ptr<string> pStr2(pStr);
cout << pStr2.unique() << endl; // false;
}
————————————————
版权声明:本文为CSDN博主「Fred^_^」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiejingfa/article/details/50750037

【C++11新特性】 C++11智能指针之shared_ptr的更多相关文章

  1. C++11 新特性之智能指针(shared_ptr, unique_ptr, weak_ptr)

    这是C++11新特性介绍的第五部分,涉及到智能指针的相关内容(shared_ptr, unique_ptr, weak_ptr). shared_ptr shared_ptr 基本用法 shared_ ...

  2. 【C++11新特性】 C++11智能指针之weak_ptr

    如题,我们今天要讲的是C++11引入的三种智能指针中的最后一个:weak_ptr.在学习weak_ptr之前最好对shared_ptr有所了解.如果你还不知道shared_ptr是何物,可以看看我的另 ...

  3. [C++11新特性] 智能指针详解

    动态内存的使用很容易出问题,因为确保在正确的时间释放内存是极为困难的.有时我们会忘记释放内存产生内存泄漏,有时提前释放了内存,再使用指针去引用内存就会报错. 为了更容易(同时也更安全)地使用动态内存, ...

  4. C++11新特性总结 (二)

    1. 范围for语句 C++11 引入了一种更为简单的for语句,这种for语句可以很方便的遍历容器或其他序列的所有元素 vector<int> vec = {1,2,3,4,5,6}; ...

  5. [转载] C++11新特性

    C++11标准发布已有一段时间了, 维基百科上有对C++11新标准的变化和C++11新特性介绍的文章. 我是一名C++程序员,非常想了解一下C++11. 英文版的维基百科看起来非常费劲,而中文版维基百 ...

  6. 在C++98基础上学习C++11新特性

    自己一直用的是C++98规范来编程,对于C++11只闻其名却没用过其特性.近期因为工作的需要,需要掌握C++11的一些特性,所以查阅了一些C++11资料.因为自己有C++98的基础,所以从C++98过 ...

  7. C++11新特性介绍 02

    阅读目录 1. 范围for语句 2. 尾置返回类型 3. =default 生成默认构造函数 4. 类对象成员的类内初始化 5. lambda表达式与bind函数 6. 智能指针share_ptr,u ...

  8. c++学习书籍推荐《深入理解C++11 C++11新特性解析与应用》下载

    百度云及其他网盘下载地址:点我 编辑推荐 <深入理解C++11:C++11新特性解析与应用>编辑推荐:C++标准委员会成员和IBM XL编译器中国开发团队共同撰写,权威性毋庸置疑.系统.深 ...

  9. c++ 11 线程池---完全使用c++ 11新特性

    前言: 目前网上的c++线程池资源多是使用老版本或者使用系统接口实现,使用c++ 11新特性的不多,最近研究了一下,实现一个简单版本,可实现任意任意参数函数的调用以及获得返回值. 0 前置知识 首先介 ...

随机推荐

  1. SQL SERVER 2012文件表(FILETABLE)新体验之一

    SQLSERVER 2012 文件表功能很COOL,让我们体验一下吧. 1,创建数据库 [sql] DREATE DATABASE FileTableTest ON  PRIMARY (     NA ...

  2. day40—JavaScript多物体运动框架

    转行学开发,代码100天——2018-04-25 今天继续学习JavaScript的运动实现——多物体运动框架的介绍及其应用. 首先来看一个简单的例子.如下图,要使图中3个红色盒子实现鼠标移入变宽,移 ...

  3. tomcat启动控制台报Exception in thread ''main".......“Could not find the main class:.....Bootstrap”问题

    startup.bat文件打开最后end下一行加pause调试,重新启动tomcat,发现配置没问题,但是依然报错,发现是jdk版本问题,jdk1.6无法与tomcat8适配,重新装个1.7版本的jd ...

  4. JavaScript搜索框响应事件

    HTML页面,注意:不要使用form标签 <input type = "text" name="keyword" id="keyword&quo ...

  5. python操作mysql之增删改查

    [insert] import MySQLdb conn = MySQLdb.connect(","08day5" ) cur = conn.cursor() #把数据放 ...

  6. Cookie/Session/Local Storage/IndexedDB

    本文主要总结客户端/浏览器端数据存储的技术. 在客户端或者浏览器端存储,可以快速的访问页面,当前主要有Cookie,Session,Local Storage,IndexedDB四种(WebSQL呗废 ...

  7. MySQL-第二篇SQL语句基础(1)语句分类及DDL语句

    1.什么是SQL语句 SQL是Structed Query Language的缩写,即结构化查询语言.SQL是操作和检索数据库的标准语言,标准的SQL语句可以操作任何关系数据库. 2.标准的SQL语句 ...

  8. P2639 [USACO09OCT]Bessie的体重问题Bessie's Weight

    题目传送门 这题和01背包最大的区别在于它没有价值,所以我们可以人工给它赋一个价值,由于要求体积最大,把价值赋成体积即可.顺带一提,这题数据范围很大,二维会MLE,要压缩成一维才可以AC 下面给出参考 ...

  9. go 学习之io/ioutil包

    // Discard 是一个 io.Writer 接口,调用它的 Write 方法将不做任何事情// 并且始终成功返回.var Discard io.Writer = devNull(0) // Re ...

  10. pthread_cond_timedwait

    该函数用于在同时等待条件变量时提供超时功能,不过该函数的超时时间是一个绝对时间.默认使用系统时间,这意味这,若修改系统时间,那么超时就不准确,有可能提前返回,也可能要几年才返回.这在某些需求下会导致b ...