effective c++ 笔记 条款13-18

条款13 以对象管理资源

对象的delete,可能因为前文的一些语句抛出异常或者过早的return(可能是最初的设计也可能多个迭代版本之后的维护导致)使得delete并没有执行,导致内存泄漏。
因此以对象管理资源。并采用RAII(Resource Acquisition Is Initialize,资源取得时机便是初始化时机,即获得资源后立刻放进管理对象),让管理对象的析构函数负责资源的释放。当对象被销毁时自动释放获取的堆资源。
C++11 中,使用std::unique_ptr管理RAII唯一所有权对象,使用std::shared_ptr引用计数来管理RAII对象

std::unique_ptr<Investment> pUniqueInv1(CreateInvestment());
std::unique_ptr<Investment> pUniqueInv2(std::move(pUniqueInv1));    // 转移资源所有权
std::shared_ptr<Investment> pSharedInv1(CreateInvestment());
std::shared_ptr<Investment> pSharedInv2(pSharedInv1);	//引用计数+1

条款14 在资源管理类小心copying行为

复制一个RAII对象时:所有资源的copying行为决定RAII对象的copying行为。
几种常见的copying行为:
1.禁止复制:多数时候RAII对象不允许被复制,需要明确禁止复制行为,条款6
2.对底层资源进行“引用计数法”:类似shared_ptr,每次复制对象计数+1,每个对象离开定义域调用析构使计数-1.为0时销毁资源
3.复制底层资源:制对象的同时复制底层资源即深拷贝。不仅复制指针,也复制指针所指数据
4.转移底层资源所有权:类似unique_ptr,只有一个对象拥有对资源的管理权,复制对象时转移管理权

条款15 在资源管理类中提供对原始资源的访问

STL 中的智能指针提供了对原始资源的隐式访问和显式访问

Investment* p = pSharedInv.get();    // 显式访问原始资源
(*pSharedInv).func();           // 隐式访问原始资源
pSharedInv->func();				// 隐式访问原始资源

设计自己的资源管理类时,也要考虑在提供对原始资源的访问,使用显式访问还是隐式访问的方法,还是两者皆可。

class Font{
   
public:
	FontHandle Get() const {
    return f; }       // 显式转换函数
    operator FontHandle() const {
    return f; }  // 隐式转换函数
private:
    FontHandle f;
}

一般而言显示转换安全,隐式转换对客户端较方便

FontHandle f2 = f1;		//原意是拷贝一个Font对象,却将f1隐式转换为底部的FontHandle,然后才复制

条款16 成对使用 new 和 delete 时要采用相同形式

使用new来分配单一对象,使用new[]来分配对象数组,必须明确它们的行为并不一致,分配对象数组时会额外在内存中记录“数组大小”,而使用delete[]会根据记录的数组大小多次调用析构函数,使用delete则仅仅只会调用一次析构函数

int* arr = new int[10];
int* obj = new int;
delete[] arr;
delete obj;

typedef定义数组类型会带来额外的风险

typedef std::string AddressLines[4];
std::string* pal = new AddressLines;    // pal 是一个对象数组,而非单一对象
delete pal;                             // 行为未定义
delete[] pal;                           // 正确

一般没必要对数组进行 typedefs,因为C++标准库中的 vector,string 等 templates,足以代替数组

条款17:以独立语句将newed对象置入智能指针

int priority();
void processWidget(std::shared_ptr<Widget> pw, int priority);
processWidget(std::shared_ptr<Widget>(new Widget()), priority());

上述调用可能造成内存泄漏.
在调用processWidget函数之前编译器会做以下三件事情:
执行new Widget()表达式动态创建Widget对象。
调用shared_ptr类的构造函数并使用Widget对象的指针作为构造参数。
调用priority函数生成优先级。
new Widget肯定在shared_ptr前,但priority可能在1,2,3任一步骤。若在步骤2执行,并发生了异常,则new Widget()返回的指针将丢失。因为在【资源被创建】和【资源被管理对象接管】之间造成了异常干扰。
解决办法:

std::shared_ptr<Widget> pw(new Widget());
processWidget(pw, priority());

编译器对跨越语句的各项操作没有重排列自由。
新的c++做法

auto pUniqueInv = std::make_unique<Investment>();    // since C++14
auto pSharedInv = std::make_shared<Investment>();    // since C++11
processWidget(std::make_shared<Widget>(), priority());

相关推荐

  1. effective c++ 笔记 13-18

    2024-02-11 01:08:01       47 阅读
  2. effective c++ 笔记 18-25

    2024-02-11 01:08:01       49 阅读
  3. effective c++ 笔记 5-12

    2024-02-11 01:08:01       56 阅读
  4. 13:用对象管理资源(智能指针)

    2024-02-11 01:08:01       40 阅读
  5. effective c++ 笔记 26-31

    2024-02-11 01:08:01       42 阅读
  6. effective c++ 笔记 49-52

    2024-02-11 01:08:01       36 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-11 01:08:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-11 01:08:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-02-11 01:08:01       82 阅读
  4. Python语言-面向对象

    2024-02-11 01:08:01       91 阅读

热门阅读

  1. vue3 watchEffect 监听子组件变化

    2024-02-11 01:08:01       58 阅读
  2. C++学习笔记——typedef和using

    2024-02-11 01:08:01       50 阅读
  3. 【算法题】91. 解码方法

    2024-02-11 01:08:01       47 阅读
  4. 常见的Web框架

    2024-02-11 01:08:01       56 阅读
  5. 笔记——asp.net core 中的 REST

    2024-02-11 01:08:01       53 阅读
  6. Ubuntu防火墙设置

    2024-02-11 01:08:01       55 阅读
  7. 带你了解软件系统架构的演变详解

    2024-02-11 01:08:01       44 阅读
  8. 假期刷题打卡--Day28

    2024-02-11 01:08:01       53 阅读
  9. 推理还原的干货

    2024-02-11 01:08:01       44 阅读
  10. js的循环遍历

    2024-02-11 01:08:01       55 阅读
  11. C语言中的宏定义:从常量到高级技巧

    2024-02-11 01:08:01       42 阅读
  12. [c/c++] 关于虚函数表

    2024-02-11 01:08:01       56 阅读