C++ 核心指南之资源管理(中)
C++ 核心指南(C++ Core Guidelines)是由 Bjarne Stroustrup、Herb Sutter 等顶尖 C++ 专家创建的一份 C++ 指南、规则及最佳实践。旨在帮助大家正确、高效地使用“现代 C++”。
这份指南侧重于接口、资源管理、内存管理、并发等 High-level 主题。遵循这些规则可以最大程度地保证静态类型安全,避免资源泄露及常见的错误,使得程序运行得更快、更好。
R.alloc: 分配和释放
- R.10: 避免使用
malloc()
/free()
- R.11: 避免显式调用
new
/delete
- R.12: 显式资源分配的结果应立即给到资源管理对象
- R.13: 在一条语句中,最多只能有一个显式资源分配
- R.14: 避免使用
[]
参数,用span
替代 - R.15: 分配/释放操作要成对重载
R.10: 避免使用 malloc()
/ free()
malloc()
/ free()
不支持构造、析构,不要和 new
/ delete
混用。
例子
class Record {
int id;
string name;
};
void use()
{
// p1 可能是 nullptr;*p1 未初始化,尤其是其中的 name 不是一个合法的 string 对象
Record* p1 = static_cast<Record*>(malloc(sizeof(Record)));
// 除非抛异常,*p2 默认初始化
auto p2 = new Record;
// p3 可能是 nullptr;如果不为空,*p3 默认初始化
auto p3 = new(nothrow) Record;
delete p1; // error: 不能 delete 由 malloc() 返回的指针
free(p2); // error: 不能 free() new 出来的对象
}
最后的 delete、free 在有的实现中可能正常工作,有的会导致运行时错误。
例外
有的应用中禁止异常,如 life-critical 和硬实时系统。但是很多针对异常的禁用只是迷信,或是担心导致旧代码资源管理上的混乱。如果是这种情况,可以考虑 nothrow
版本的 new
代码检查建议
标记显式的 malloc/free 调用
R.11: 避免显式调用 new
/ delete
new 返回的指针应该属于资源句柄(在资源句柄的析构中自动调用 delete)。如果 new 返回值赋给了裸指针,可能导致资源泄露。
注
在大型项目中,如果在应用代码中(而不是在专门资源管理类中)出现 delete,那多半会有 bug:如果代码里有几处 delete 调用,你怎么保证没有多调用或者少调用?这类 bug 不一定能立即发现,可能在潜伏一段时间后,在某次代码维护/重构时暴露。
代码检查建议
针对显式的 new / delete 给出警告,建议使用 make_unique 替代
R.12: 显式资源分配的结果应立即给到资源管理对象
否则,一旦抛异常或返回将导致资源泄露
反面例子
void func(const string& name)
{
// 打开文件
FILE* f = fopen(name, "r");
vector<char> buf(1024);
// 关闭文件
auto _ = finally([f] { fclose(f); });
// ...
}
buf 分配空间可能失败抛异常,导致 f 文件句柄泄露
正面例子
void func(const string& name)
{
ifstream f{name};
vector<char> buf(1024);
// ...
}
文件句柄在 ifstream 内部,ifstream 销毁时自动 fclose 文件句柄,简单、安全、高效。
代码检查建议
标记那些用来初始化指针的显式资源分配
R.13: 在一条语句中,最多只能有一个显式资源分配
如果在一条语句中执行两个显式资源分配,可能导致资源泄露。因为很多子表达式的求值顺序(包括函数参数)是未定义的。
例子
void fun(shared_ptr<Widget> sp1, shared_ptr<Widget> sp2);
如果像下面这样调用 fun()
:
// BAD: 可能泄露
fun(shared_ptr<Widget>(new Widget(a, b)), shared_ptr<Widget>(new Widget(c, d)));
上述调用是“异常不安全”(exception-unsafe)的,因为编译器可能会对创建两个参数的表达式重新排序。特别是编译器可能交叉执行两个子表达式:先给 sp1、sp2 分配内存空间、然后调用 Widget 的构造。如果此时在构造某一个参数的时候抛出异常,则另一个对象的内存不会被释放!
解决这个问题也很简答,不在一条语句里出现多个显式资源分配即可。例如;
// 稍好,但有点乱
shared_ptr<Widget> sp1(new Widget(a, b));
fun(sp1, new Widget(c, d));
最好的办法是完全避免显式资源分配,而是通过工厂函数返回拥有的对象:
// 最佳实践
fun(make_shared<Widget>(a, b), make_shared<Widget>(c, d));
如果没有像 make_shared、make_unique 这样的工厂函数,自己封装一个。
代码检查建议
如果一条语句内有多个显式资源分配,标记该语句
R.14: 避免使用 []
参数,用 span
替代
数组形参退化为指针,丢失数组大小信息,容易导致边界错误。用 span
可以保留数组大小信息。
例子
// 不推荐
void f(int[]);
// 不推荐指针指向多个对象
// 指针应该指向单个对象(见 R.2)
void f(int*);
// 推荐
void f(gsl::span<int>);
R.15: 分配/释放操作要成对重载
否则将导致混乱
例子
class X {
void* operator new(size_t s);
void operator delete(void*);
};
注
如果希望内存不被释放,用 =delete
明确禁止释放操作。
代码检查建议
标记不成对的分配/释放操作
C++ 核心指南之资源管理(中)的更多相关文章
- Robot Framework自动化测试框架核心指南-如何做好自动化测试平台框架的设计
自动化测试如果需要能高效快速的支撑软件项目的测试,项目的快速迭代以及上线,除了以上我们介绍的需要许多的Lib来支持以及需要高效的去编写自动化测试案例外,还需要一个好的自动化测试框架平台来支撑我们的自动 ...
- 三年磨一剑,robot framework 自动化测试框架核心指南,真正讲透robot framework自动化测试框架(笔者新书上架)。
序 关于自动化测试的工具和框架其实有很多.自动化测试在测试IT行业中扮演着越来越重要的角色,不管是在传统的IT行业还是高速发展的互联网行业或是如今的大数据和大热的人工智能领域,都离不开测试,也更加离不 ...
- Robot Framework自动化测试框架核心指南-如何使用Java编写自定义的RobotFramework Lib
如何使用Java编写自定义的RobotFramework Lib 本文包括2个章节 1. Robot Frdamwork中如何调用java Lib库 2.使用 java编写自定义的Lib 本文作者为: ...
- Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍
1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...
- Java核心知识点学习----线程中如何创建锁和使用锁 Lock,设计一个缓存系统
理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...
- JavaScript权威指南--WEB浏览器中的javascript
知识要点 1.客户端javascript window对象是所有客户端javascript特性和API的主要接入点.它表示web浏览器的一个窗口或窗体,并且可以用window表示来引用它.window ...
- Github使用指南(学习中随时更新)
注册好一个账号后先创建一个仓库 点击"Create repository"创建一个版本库 填好带*号的必填项,选择是要公开仓库还是私人使用,勾选自动添加README选项 READM ...
- Linux可信计算机制模块详细分析之核心文件分析(8)tpm.c核心代码注释(中)
/*设置TPM命令格式*/ ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap, const char *desc) ...
- Java核心知识点学习----线程中的Semaphore学习,公共厕所排队策略
1.什么是Semaphore? A counting semaphore. Conceptually, a semaphore maintains a set of permits. Each acq ...
- JSTL-core核心代码标签库中的forEach,remove, forTokens,choose,when,otherwise,redirect 标签
<%@ page language="java" import="java.util.*, cn.hncu.domain.*" pageEncoding= ...
随机推荐
- 在k8s(kubernetes) 上安装 ingress V1.1.0
Ingress 公开了从集群外部到集群内服务的 HTTP 和 HTTPS 路由.流量路由由 Ingress 资源上定义的规则控制. 下面是一个将所有流量都发送到同一 Service 的简单 Ingre ...
- 实现自定义注解,实现ioc与aop
实现自定义注解主要分三个步骤: 1.定义自己的注解类. 注解类默认继承Annotation接口. 且注解内的属性默认public(要给别人用的) 属性可以定义默认值也就是不给你的属性传值也会拥有默认. ...
- pysimplegui之进度表one_line_progress_meter
我们的代码中都有循环.'等待,看着文本窗口中滚动过去的计数器不是很快乐吗?一行代码如何获得一个进度表,其中包含有关您的代码的统计信息? one_line_progress_meter(title, c ...
- js如何引入高德地图API?
准备 成为开发者并创建 key 1.登录控制台 登录 高德开放平台控制台,如果没有开发者账号,请 注册开发者. 2.创建 key 进入应用管理,创建新应用,新应用中添加 key,服务平台选择 Web端 ...
- java RSA生成公钥和私钥
1.随机生成密钥对 /** * 随机生成密钥对 * @throws NoSuchAlgorithmException */ public static void genKeyPair() throws ...
- 第一章:PyTorch 入门
第一章:PyTorch 入门 1.1 Pytorch 简介 1.1.1 PyTorch的由来 1.1.2 Torch是什么? 1.1.3 重新介绍 PyTorch 1.1.4 对比PyTorch和Te ...
- [人脸活体检测] 论文:Learn Convolutional Neural Network for Face Anti-Spoofing
[人脸活体检测] 论文:Learn Convolutional Neural Network for Face Anti-Spoofing 论文简介 这是最早期CNN用于活体检测的文章,在此之前都是手 ...
- Centos7.x 安装jenkins
一.安装 前提:需查看是否安装了JDK 1.第一种方法 sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat ...
- 几行代码教你快速创建scrapy项目,非常实用建议收藏!
import shutil,os修改settings.py def config(scrapy_path,project_name): judge=input("是否自动修改配置?是:yes ...
- Solon 统一的返回结果调整
使用 "统一的渲染控制" 可以对输出做统一的控制外...还可以借助路由拦截器 RouterInterceptor ,对 mvc 返回结果做提交确认机制(即可修改)进行控制(相对来讲 ...