C++ 借来的资源,如何还的潇洒?

蓝月亮
发布于 2020-9-2 15:01
浏览
0收藏

所谓的资源就是,一旦用了它,将来必须还给系统。如果不是这样,糟糕的事情就会发生。

 

C++ 程序内常见的资源:

动态分配内存
文件描述符
互斥锁
图形页面中的字型和笔刷
数据库连接
网络 sockets

无论哪一种资源,重要的是,当你不再使用它时,必须将它还给系统,有借有还是个好习惯。

 

细节 01 : 以对象管理资源

把资源放在析构函数,交给析构函数释放资源

「智能指针」是个好帮手,交给它去管理指针对象。

对于是由动态分配(new)于堆内存的对象,指针对象离开了作用域并不会自动调用析构函数(需手动delete),为了让指针对象能像普通对象一样,离开作用域自动调用析构函数回收资源,我们需要借助「智能指针」的特性。

 

常用的「智能指针」有如下三个:

std::auto_ptr( C++ 98 提供、C++ 11 建议摒弃不用 )
std::unique_ptr( C++ 11 提供 )
std::shared_ptr( C++ 11 提供 )

 

「以对象管理资源」的两个关键想法:

获得资源后立刻放进管理对象内。

管理对象运用析构函数确保资源释放。

 

为防止资源泄漏,请使用 RAII(Resource Acquisition Is Initaliaztion - 资源取得时机便是初始化时机) 对象,它们在构造函数中获取资源,并在析构函数中是释放资源
两个建议使用的 RAII classes 分别是 std::uniqueptr 和 std::sharedptr。前者不允许 copy 动作,后者允许 copy 动作。但是不建议用 std::autoptr,若选 autoptr,复制动作会使它(被复制物)指向 null 。

 

细节 02:在资源管理类中小心 copying 行为

复制 RAII 对象必须一并复制它的所管理的资源(深拷贝),所以资源的 copying 行为决定 RAII 对象的 copying 行为。
普通而常见的 RAII class copying 行为是:禁止 copying、施行引用计数法

 

细节 03 :在资源类中提供对原始资源的访问

智能指针「显式」转换,也就是通过 get 成员函数的方式转换为原始指针对象。

上面提到的「智能指针」分别是:std::autoptr、std::uniqueptr、std::shared_ptr。它们都有访问原始资源的办法,都提供了一个 get 成员函数,用来执行显式转换,也就是它会返回智能指针内部的原始指针(的复件)。

 

智能指针「隐式」转换的方式,是通过指针取值操作符。

智能指针都重载了指针取值操作符(operator->和operator*),它们允许隐式转换至底部原始指针:

多数设计良好的 classes 一样,它隐藏了程序员不需要看到的部分,但是有程序员需要的所有东西。

所以对于自身设计 RAII classes 我们也要提供一个「取得其所管理的资源」的办法。

 

APIs 往往要求访问原始资源,所以每一个 RAII class 应该提供一个「取得其所管理的资源」的办法。
对原始资源的访问可能经由显式转换或隐式转换。一般而言显式转换比较安全,但隐式转换比较方便。

 

细节 04:成对使用 new 和 delete 

当使用 new ,有两件事发生:

内存被分配出来(通过名为 operator new 的函数)
针对此内存会有一个或多个构造函数被调用

当使用 delete,也会有两件事情:

针对此内存会有一个或多个析构函数被调用
然后内存才被释放(通过名为 operator delete 的函数)

delete 的最大问题在于:即将被删除的内存之内究竟有多少对象?这个答案决定了需要执行多少个析构函数。

 

对象数组所用的内存通常还包括「数组大小」的记录,以便 delete 知道需要调用多少次析构函数。单一对象的内存则没有这笔记录。你可以把两者不同的内存布局想象如下,其中 n 是数组大小:

 

当你对着一个指针使用 delete,唯一能够让 delete 知道内存中是否存在一个「数组大小记录」的办法就是:由你告诉它。如果你使用 delete 时加上中括号[],delete 便认定指针指向一个数组,否则它便认定指针指向一个单一对象:

 

游戏规则很简单:

如果你在 new 表达式中使用[],必须在相应的 delete 表达式也使用[]。
如果你在 new 表达式中不使用[],一定不要在相应的 delete 表达式使用[]。

 

细节 05:以独立语句将 newed (已被 new 的)对象置入智能指针

以独立语句将 newed (已 new 过) 对象存储于智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄漏。

 

分类
标签
收藏
回复
举报
回复
    相关推荐