Home 资源管理
Post
Cancel

资源管理

以对象管理资源

当我们向系统申请某一笔资源后, 应该立刻在同一语句内用它来初始化某个管理对象. 如果有下面这个工厂函数用来动态分配一个资源, 并返回指向资源的指针:

1
Resource* create_resource();

大部分时候我们可以通过智能指针很好的管理资源, 例如下面的代码:

1
std::shared_ptr<Resource> ptr(create_resource());

这样做的好处是将返回的指针放入智能指针对象中进行管理, 这样当该智能指针对象的生存期结束后, 将会在其析构函数中自动释放其持有的资源. 如果不这么做, 用户就需要自己通过 delete 来释放资源, 这会明显增加内存泄漏的风险.

另外值得注意的是, 不要试图用智能指针去管理动态分配的数组, 例如下面的代码:

1
std::shared_ptr<int> ptr(new int[1024]);

这行代码中试图用 shared_ptr 来管理一个包含 1024 个元素的整型数组, 这不会导致编译错误. 但是在 shared_ptr 等智能指针的析构函数中使用的是 delete 操作而非 delete[], 这会导致有一部分资源没有被释放.

如果在 new 表达式中使用了 [], 那么一定要在相应的 delete 表达式中也使用 []. 反之亦然.

复制资源管理类时要小心

由于在资源管理类中往往持有指向资源的指针, 因此在对其进行复制时需要谨慎应对. 通常我们应该考虑该资源是否能被复制, 该如何被复制, 这直接决定了资源管理类的复制方式. 常用的处理手段有如下几种:

  • 禁止复制
  • 采用引用计数法, 类似 shared_ptr
  • 复制底部资源(深拷贝)
  • 转移底部资源的所有权, 类似 unique_ptr

newed 对象放入智能指针时应使用一条独立的语句

考虑下面这两个函数, 前者可以为后者提供 prio 参数:

1
2
int priority();
void process_widget(std::shared_ptr<Widget> pw, int prio);

如果我们用下面的方式来调用后者:

1
process_widget(std::shared_ptr<Widget>(new Widget), priority());

在调用该函数之前, 编译器首先要生成函数的实参, 这个过程中包含三个步骤:

  • 执行 new Widget
  • 调用 std::shared_ptr 构造函数
  • 调用 priority() 函数

而 C++ 以什么顺序来完成这三个步骤是不能确定的, 如果编译器采用了下面的方式:

  1. 执行 new Widget
  2. 调用 priority() 函数
  3. 调用 std::shared_ptr 构造函数

在这种情况下, 一旦 priority() 函数中出现异常, 那么第一步中得到的指针将会遗失, 这将导致内存泄漏. 为了避免这种问题, 只需要将生成智能指针的部分代码分离成一条单独的语句即可:

1
2
std::shared_ptr<Widget> pw(new Widget);
process_widget(pw, priority());

这样做是因为编译器只有在 同一条语句 内才有重新排列各项操作的自由.

This post is licensed under CC BY 4.0 by the author.