作者:天蝎快乐公主_594 | 来源:互联网 | 2023-09-11 23:18
资源管理并不单指内存,这里的资源包括内存、文件描述器、互斥锁、图形界面中的字形与笔刷、数据库连接、网络sockets等,其中最常用的是内存资源。13.以对象管理资源为防止资源泄露的
资源管理并不单指内存,这里的资源包括内存、文件描述器、互斥锁、图形界面中的字形与笔刷、数据库连接、网络sockets等,其中最常用的是内存资源。
13.以对象管理资源
为防止资源泄露的一个简单方法是:使用RAII对象,(资源取得即初始化),通常在构造函数中分配资源,在析构函数中释放资源。这样做带来的好处是:对象在离开作用域时,会隐式调用析构函数,将内存释放,不必担心忘记将资源释放及提早return或出现异常导致资源未释放的问题。
RAII的一个例子是智能指针auto_ptr,智能指针的一个特点是:由于智能指针被销毁时会自动释放指向的内存,因此不允许两个指针指向同一块内存。在指针赋值时,ptr1=ptr2,ptr1接受ptr2的赋值后,ptr2会变成NULL,ptr1拥有该资源的唯一控制权,这一特性不同于普通指针。
若要完成两个指针指向同一内存的功能,可以使用shared_ptr,称为引用技术性智慧指针(RCSP),它追踪共有多少对象指向某笔资源,并在无人指向时释放该资源。
值得说明的一点是:使用智能指针完成动态分配数组是一个糟糕的想法,因为智能指针析构时调用的是delete而非delete[ ],这将只释放掉数组的头指针,而数组内存无法释放。若你有该需求,使用vector是个好的选择。
14.资源管理类中小心copy行为
复制RAII对象必须一并复制它所管理的资源,所以资源的copy行为决定了RAII对象的copy行为。当一个RAII对象被复制时,我们通常会考虑下面的策略:
(1)禁止复制
对于一些RAII对象,对其进行复制操作事实上并不合理(如互斥锁),此时我们应该禁止复制,实现方法之前有详述(重载复制构造函数设为private)。
(2)对底层资源使用“引用计数法”
有时候我们希望保留某资源,直至其最后一个调用者被销毁,类似于shared_ptr的机制。
这里的shared_ptr允许指定第二个参数,将一函数作为删除器,在调用者计数为零时,将会调用该删除器完成相应功能(本例中是解开互锁)
(3)复制底部资源
这要求复制时不仅复制资源管理对象,同时也复制其所包覆的资源。这种复制方式称为“深拷贝”。
(4)转移底部资源
auto_ptr是一个典型。
15.在资源管理类中提供对原始资源的访问
RAII给我们提供了一种管理内存的机制,但有时我们经常会遇到需要访问原始资源的问题,因此每一个RAII类应当提供一种访问其原始资源的方法如get()函数。对原始资源的访问可能经由显式转换或隐式转换,一般来说显式转换比较安全,但隐式转换对用户来说比较方便。
16.成对使用new和delete时要采取相同形式
delete与new一一匹配,delete[ ]与new一个数组一一匹配。
(来自C++ primer)
当new函数运行时,实际上发生三个步骤:
1.调用operator new的标准库函数,分配足够大的原始未类型化的内存,以保存指定类型的一个对象。
2.运行该类型的一个构造函数,使用指定初始化式构造对象
3.返回指向新分配并构造对象的指针
当delete函数运行时,实际上发生两个步骤:
1.对指针指向的对象调用适当的析构函数
2.调用operator delete的标准库函数释放对象使用的内存
17.以独立语句将newd对象置入智能指针
为什么上图的做法不是一个好的选择?
因为按需要本应按下图的顺序执行工作:
但C++并未定义其顺序,编译器完全可能以下面的顺序完成
此时问题出现了,若第二步出现异常,则第三步不会运行,作为唯一能够控制该内存的智能指针并未建立,该块内存将永远不能被我们访问与释放。
因此我们应当将使用独立语句将其置入智能指针,以避免出现异常导致句柄丢失影响内存释放。