首页 > 编程语言 >C++ 核心指南之资源管理(中)

C++ 核心指南之资源管理(中)

时间:2023-06-25 21:00:13浏览次数:45  
标签:指南 shared void C++ 显式 new 资源分配 资源管理 delete

C++ 核心指南之资源管理(中)

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 明确禁止释放操作。

代码检查建议

标记不成对的分配/释放操作

标签:指南,shared,void,C++,显式,new,资源分配,资源管理,delete
From: https://www.cnblogs.com/tengzijian/p/17503953.html

相关文章

  • JavaScript 中的求幂:初学者指南
    介绍求幂是指将一个数乘以另一个数的幂的数学过程。例如,如果我们求2的次方3,我们将其计算为2*2*2,这会得到的结果8。在JavaScript中,计算指数时可以使用**ES6中引入的运算符或方法。Math.pow()使用**运算符该**运算符用于在JavaScript中执行求幂运算。它需要两个操作数:底......
  • 58 KVM工具使用指南-应用 LibcarePlus 热补丁
    58KVM工具使用指南-应用LibcarePlus热补丁本节以原文件foo.c和补丁文件bar.c为例,介绍LibcarePlus热补丁的应用指导。58.1前期准备应用LibcarePlus热补丁之前,需要提前准备好原可执行程序foo、以及热补丁文件foo.kpatch。58.2加载热补丁本节介绍应用LibcarePlus......
  • C++ 数据抽象
     数据抽象是指,只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节。数据抽象是一种依赖于接口和实现分离的编程(设计)技术。让我们举一个现实生活中的真实例子,比如一台电视机,您可以打开和关闭、切换频道、调整音量、添加外部组件(如喇叭、录像机、DVD播......
  • ASEMI快恢复二极管MUR80100PT功能和应用实用指南
    编辑-ZMUR80100PT是一种高性能、超快恢复二极管,设计用于各种应用,包括电源、逆变器和电机控制系统。本文将提供一个全面的指南,以了解MUR80100PT的特点和应用,以及它在提高电子设备的效率和可靠性方面的重要性。 MUR80100PT的特点 1.超快恢复时间:MUR80100PT拥有仅35ns的超快恢复时间......
  • ASEMI快恢复二极管MUR80100PT功能和应用实用指南
    编辑-ZMUR80100PT是一种高性能、超快恢复二极管,设计用于各种应用,包括电源、逆变器和电机控制系统。本文将提供一个全面的指南,以了解MUR80100PT的特点和应用,以及它在提高电子设备的效率和可靠性方面的重要性。 MUR80100PT的特点 1.超快恢复时间:MUR80100PT拥有仅35ns的超快恢......
  • C/C++C/C++语言课程设计参考题目[2023-06-25]
    C/C++C/C++语言课程设计参考题目[2023-06-25]《高级程序设计技术综合课程设计》C/C++语言课程设计参考题目目录信息管理类 1工资管理系统 1职工工资信息管理系统 2职工工资管理系统 2院职工工资管理系统 3公司职工工资管理系统 3工资纳税计算系统 3校际运动会管理系统......
  • MongoDB常用命令指南
    定义......
  • C++面试八股文:std::string是如何实现的?
    C++面试八股文:std::string是如何实现的?某日二师兄参加XXX科技公司的C++工程师开发岗位第18面:面试官:std::string用过吧?二师兄:当然用过(废话,C++程序员就没有没用过std::string的)。面试官:std::string("hello")+"world"、"hello"+std::string("world")和std::string("hello")......
  • 57 KVM工具使用指南-制作 LibcarePlus 热补丁
    57KVM工具使用指南-制作LibcarePlus热补丁57.1概述LibcarePlus支持如下方式制作热补丁:手动制作通过脚本制作手动制作热补丁的过程繁琐,对于代码量较大的工程,例如QEMU,手动制作热补丁极其困难。建议使用LibcarePlus自带脚本一键式地生成热补丁文件。57.2手动制作本节......
  • Qt/c++类型转换总结
    1、static_cast、dynamic_cast、强制转换(reinterpret_cast和const_cast)、qobject_cast、qstatic_cast以及qdynamic_cast的区别:static_cast:用于执行静态类型转换,可以进行常见的类型转换,例如基本类型之间的转换、向上转型、向下转型(但没有运行时类型检查)等。在C++中......