C++ 中的智能指针-基础
简介
在现代 C++ 编程中,标准库包含了智能指针(Smart pointers)。
智能指针用来确保程序不会出现内存和资源的泄漏,并且是"异常安全"(exception-safe)的。
智能指针的使用
智能指针定义在头文件 memory 里的命名空间 std 中。它对于资源获取即初始化(RAII, Resource Acquisition Is Initialization) 编程理念至关重要。该理念的目的是保证对象初始化的时候也是资源获取的时候,从而使对象的所有资源在单行代码中创建。
实践中,RAII 的主要原则就是把任何在堆上分配的资源(比如动态分配的内存或者系统对象的处理)的所有权提供给在栈上分配的对象(其析构函数包含释放资源及相关清理的代码)。
大多数时候,当你初始化一个原始指针或者资源句柄使其指向实际的资源时,立即将其传给智能指针。
在现代 C++ 中,原始指针只用于包含在局部作用域,循环或者工具函数的小块代码中(对性能有要求,并且对资源的所有权也不容易混淆)。
原始指针和智能指针的声明比较如下:
void UseRawPointer()
{
// Using a raw pointer -- not recommended.
Song* pSong = new Song(L"Nothing on You", L"Bruno Mars");
// Use pSong...
// Don't forget to delete!
delete pSong;
}
void UseSmartPointer()
{
// Declare a smart pointer on stack and pass it the raw pointer.
unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars"));
// Use song2...
wstring s = song2->duration_;
//...
} // song2 is deleted automatically here.
如上所示,智能指针是一个在栈上声明的类模板,并由指向分配在堆上的对象的原始指针初始化。当智能指针初始化后,它就拥有了原始指针的所有权。这意味着智能指针需要负责原始指针指向的内存释放。智能指针的析构函数包含了 delete 的调用,并且由于智能指针是在栈上声明的,其析构函数会在智能指针对象离开作用域时被调用,即使在栈中发生了异常。
通过使用指针运算符(-> 和 *)访问被封装的指针,智能指针类重载了这些运算符以返回被封装的原始指针。
C++ 智能指针的理念类似于在 C# 语言中创建对象的过程:创建对象后让系统负责在正确的时间将其删除。不同之处在于,没有独立的垃圾回收器运行于后台;内存是按照标准 C++ 规范对内存进行管理的,使运行时环境更加快速和高效。
[!重要]
总是在单独的行上创建智能指针,而不是在参数列表中,从而避免由于特定的参数列表分配规则出现一些轻微的内存泄漏
以下示例显示了 C++ 标准库中的 unique_ptr 是如何封装指向大型对象的指针的。
class LargeObject
{
public:
void DoSomething(){}
};
void ProcessLargeObject(const LargeObject& lo){}
void SmartPointerDemo()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Pass a reference to a method.
ProcessLargeObject(*pLarge);
} //pLarge is deleted automatically when function block goes out of scope.
上述示例演示了使用智能指针的关键步骤:
- 将智能指针声明为局部变量(不要在智能指针上使用
new或者malloc表达式)。 - 在类型参数上,指定被封装指针指向的对象类型。
- 将指向由
new创建的对象的指针传给智能指针的构造函数。 - 使用重载的操作符
->和*来访问对象。 - 让智能指针来
delete对象。
智能指针在设计上兼顾了内存和性能的高效性。例如,unique_ptr 唯一的数据成员是被封装的原始指针,这意味着 unique_ptr 具有原始指针同样地大小,4 字节或者 8 字节。通过智能指针重载的操作符 -> 和 * 来访问并不比直接使用原始指针来访问慢多少。
智能指针有其自己的成员函数,通过 . 来访问。例如,一些 C++ 标准库的智能指针有用于重置的成员函数来释放对原始指针的所有权。这可以用于在智能指针超出作用域前释放智能指针管理的内存,看下面的示例:
void SmartPointerDemo2()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Free the memory before we exit function block.
pLarge.reset();
// Do some other work...
}
智能指针通常提供了获取原始指针的方式。 C++ 标准库中的智能指针包含了成员函数 get 来获取原始指针。 CComPtr 有公共的类成员 p。通过获取原始指针,你能够使用智能指针来管理你自己代码涉及的内存并依然能够将原始指针传递给不支持智能指针的代码。
void SmartPointerDemo4()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Pass raw pointer to a legacy API
LegacyLargeObjectFunction(pLarge.get());
}
智能指针的种类
以下部分总结了在 Windows 环境下不同种类的智能指针,以及如何使用它们。
C++ 标准库中的智能指针
优先使用下列智能指针来封装原始指针指向的纯旧对象(plain old C++ objects,POCO):
unique_ptr- 对封装的原始指针是独占的
- 默认用于 POCO,除非你明确的知道你需要一个
shared_ptr - 可以移入新的所有者,但不能拷贝或者共享
- 替代
auto_ptr,auto_ptr已作废 - 对比
boost::scoped_ptr,unique_ptr更加小巧和高效 - 长度为一个指针的大小,并且支持右值引用来快速执行 C++ 标准库容器的插入和遍历操作
shared_ptr- 引用计数智能指针
- 当你需要将原始指针分派给多个所有者时使用,例如,当你从容器返回一个指针的拷贝并且想要保留它
- 原始指针不会被
delete直到所有的shared_ptr超出作用域或者放弃所有权。 - 长度为两个指针的大小,一个用于对象,另一个用于包含引用计数的共享控制块
weak_ptr- 结合
shared_ptr使用的特殊智能指针。 weak_ptr提供了对被一个或者多个shared_ptr所拥有的对象的访问,但不参与引用计数。- 如果你想要监测某个对象,不要求其不被释放,可以使用
weak_ptr - 在某些情况下,用于解决
shared_ptr实例间的循环引用。
- 结合
扩展
- 用于 COM 组件的智能指针
- 用于 POCO对象的ATL智能指针
引用
C++ 中的智能指针-基础的更多相关文章
- OSG中的智能指针
在OpenSceneGraph中,智能指针(Smart pointer)的概念指的是一种类的模板,它针对某一特定类型的对象(即Referenced类及其派生类)构建,提供了自己的管理模式,以避免因为用 ...
- RPCZ中的智能指针单例
RPCZ中的智能指针单例 (金庆的专栏) 智能指针单例应用于 RPCZ 库以实现库的自动初始化与自动清理. RPCZ: RPC implementation for Protocol Buffers ...
- Boost中的智能指针(转)
这篇文章主要介绍 boost中的智能指针的使用.(转自:http://www.cnblogs.com/sld666666/archive/2010/12/16/1908265.html) 内存管理是一 ...
- C++中的智能指针、轻量级指针、强弱指针学习笔记
一.智能指针学习总结 1.一个非const引用无法指向一个临时变量,但是const引用是可以的! 2.C++中的delete和C中的free()类似,delete NULL不会报"doubl ...
- ATL和vc++中的智能指针(分别是CComPtr和_com_ptr_t)
一.智能指针的概念 智能指针是一个类,不是指针,智能指针在所包含的指针不再被使用时候会自动释放该所包含指针所占用的系统资源,而不用手动释放. 原理:智能指针封装了包含指针的AddRef()函数和Rel ...
- 标准库中的智能指针shared_ptr
智能指针的出现是为了能够更加方便的解决动态内存的管理问题.注:曾经记得有本书上说可以通过vector来实现动态分配的内存的自动管理,但是经过试验,在gcc4.8.5下是不行的.这个是容易理解的,vec ...
- 智能指针类模板(中)——Qt中的智能指针
Qt中的智能指针-QPointer .当其指向的对象被销毁时,它会被自动置空 .析构时不会自动销毁所指向的对象-QSharedPointer .引用计数型智能指针 .可以被自由的拷贝和赋值 .当引用计 ...
- 智能指针类模板(上)——STL中的智能指针
智能指针类模板智能指针本质上就是一个对象,它可以像原生指针那样来使用. 智能指针的意义-现代C++开发库中最重要的类模板之一-C++中自动内存管理的主要手段-能够在很大程度上避开内存相关的问题 1.内 ...
- C++中的智能指针类模板
1,智能指针本质上是一个对象,这个对象可以像原生的指针一样使用,因为智能指 针相关的类通过重载的技术将指针相关的操作符都进行了重载,所以智能指针对象可以像原生指针一样操作,今天学习智能指针类模板,通过 ...
随机推荐
- 倍增小结 ST 与 LCA
倍增 倍增我是真滴不会 倍增法(英语:binary lifting),顾名思义就是翻倍. 能够使线性的处理转化为对数级的处理,大大地优化时间复杂度. (ps:上次学倍增LCA,没学会,老老实实为了严格 ...
- 11.15 gryz校测(题解分析报告)
T1 心有灵犀 (cooperate) 题目大意 给你一个不超过 \(10^9\) 的数字 \(n\) 和一个交换次数上限 \(k\), 每次操作对这个 数字 \(n\) 的其中两位进行交换, 比如 ...
- js--数组的find()和findIndex()方法的使用介绍
前言 阅读本文前先来思考一个问题,面对一个非空数组,你如何快速对数组进行遍历,如何快速找到数组中第一个我们需要关注的数据元素,并且如何知道该元素在数组中对应的下标索引,可能用for循环遍历,然后判断元 ...
- 数据分析中常用的Excel函数
数据分析中excel是一个常见且基础的数据分析工具,要想掌握好它,学会使用其中的常用函数是一个绕不过去的坎.从网上搜集的资料来说,基本上确定了数据分析中Excel的常用函数有以下这六类 数学函数:SU ...
- java开发工具一个很好的注释模板
<?xml version="1.0" encoding="UTF-8" standalone="no"?><templa ...
- 关于Java注解(annotation)的简单理解
一.什么是注解? 从 JDK5 开始,Java增加对元数据的支持,也就是注解.简单理解就是代码里的特殊标志,这些标志可以在编译,类加载,运行时被读取,并执行相应的处理,以便于其他工具补充信息或者进行部 ...
- 2019牛客暑期多校训练营(第五场)E.independent set 1(状压dp)
题意:给你n个点 m条边 问你所有子图的最大独立集的和 思路:我们可以设f state 为当前点集下的最大独立集的大小 所以我们可以把集合分为两个部分 绝对包含了这个一个点 绝对不包含这个点 两种情况 ...
- Luogu T9376 区间GCD
题目背景 无 题目描述 给定一长度为n的动态序列,请编写一种数据结构,要求支持m次操作,包括查询序列中一闭区间中所有数的GCD,与对一闭区间中所有数加上或减去一个值. 输入输出格式 输入格式: 第1行 ...
- hoj2430 Counting the algorithms
My Tags (Edit) Source : mostleg Time limit : 1 sec Memory limit : 64 M Submitted : 725, Acce ...
- HTTP的传输编码(Transfer-Encoding:chunked) / net::ERR_INVALID_CHUNKED_ENCODING
https://blog.csdn.net/m0_37668842/article/details/89138733 https://www.cnblogs.com/jamesvoid/p/11297 ...