做网站看什么书,百度推广去哪里学技术,天辰建设网站,wordpress图插件三. 资源管理
13. 以对象管理资源
请记住#xff1a;
为防止资源泄露#xff0c;使用智能指针
14. 在资源管理类中小心copying行为
请记住#xff1a;
复制RAII对象必须一并复制他所管理的资源#xff0c;所以资源的copying行为决定RAII对象的copying行为普遍而常见的…三. 资源管理
13. 以对象管理资源
请记住
为防止资源泄露使用智能指针
14. 在资源管理类中小心copying行为
请记住
复制RAII对象必须一并复制他所管理的资源所以资源的copying行为决定RAII对象的copying行为普遍而常见的RAII class copying行为是抑制copying、施行引用计数法reference counting。不过其他行为也都可能被实现。
解释 RAII资源获取即初始化是C中一个非常重要的概念它用于管理对象生命周期内的资源分配和释放问题。
对于复制RAII对象首要问题就是如何处理它所管理的资源。实际上资源的复制行为决定了RAII对象的复制行为不同的策略会对管理资源的对象的复制产生不同的影响
抑制复制Prohibit Copying既简单又有效。在这种情况下复制构造函数和赋值运算符通常被声明为private并且不传递任何实现。这阻止了复制和赋值。这是一个好的默认行为通常适用于管理不可共享资源的类如文件、线程、互斥锁等。
class Uncopyable {
private:Uncopyable(const Uncopyable);Uncopyable operator(const Uncopyable);
};引用计数Reference Counting这种方法允许复制同时确保原始资源的生命周期恰到好处。具体来说只有当最后一个引用被销毁时才会清理底层资源。std::shared_ptr是一种典型的实现引用计数的RAII类。
std::shared_ptrint p(new int[10]);
std::shared_ptrint q p; // 引用计数增加深度复制Deep Copying在这种策略中我们为每个RAII对象创建一份新的资源副本。这比引用计数更安全但可能更为费时因为它需要复制所有数据。 转移资源所有权Transfer Ownership通过移动语义C11开始提供可以将资源从一个对象转移到另一个对象而不需要复制。这通常适用于管理大块资源的RAII对象例如std::unique_ptr。
根据特定的类管理的资源类型和预期的用途可以选择适合的策略来处理RAII对象的复制行为。这是面向对象的设计的一个重要方面。
15. 在资源管理类中提供对原始资源的访问
请记住
APIs往往要求访问原始资源所以每一个RAI class应该提供一个“取得其所管理之资源”的办法。对原始资源的访问可能经由显式转换或隐式转换。一般而言显示转换比较安全但隐式转换对客户比较方便。
解释 资源获取即初始化RAII的类确实经常需要提供获取其管理的资源的方式。这样的设计并不会违背RAII原则因为资源的所有权仍然在RAII对象中但一些情况下确实需要让程序员能够访问这些底层资源。
这些底层资源的访问方式主要分为两种
显式转换这样的设计使得访问者必须显示地请求访问资源。这样可以减少因误用底层资源而导致的问题。std::unique_ptr就是一个很好的例子它提供了.get()方法来显式获取底层的原始指针。
std::unique_ptrint p(new int());
int* raw p.get();隐式转换这提供了对使用者更为简单的访问方式但是如果使用不当可能带来风险。比如std::shared_ptr可以被隐式转化为bool类型这使得我们能够在条件语句中使用。
std::shared_ptrint p(new int());
if (p) {// 如果 p 管理着一个对象那么将会执行这里的代码
} 一般来说显式转换更为安全因为它强制要求使用者意识到他们正在直接操作底层资源。然而隐式转换可以使代码更简洁提供更好的用户体验。因此应根据特定的使用场景和对安全性的要求来确定哪种方法更为合适。
16. 成对使用new和delete时要采取相同形式
请记住
如果你在new表达式中使用[]必须在相应的delete表达式中也使用[]。如果你在new表达式中不使用[]一定不要在相应的delete表达式中使用[]。
解释 在C中这个规则非常重要因为不正确地使用new[]和delete[]会导致未定义的行为。
当我们使用new[]来分配数组时必须使用delete[]来删除该数组。如果我们只用delete就可能会出现内存泄漏。
int* array new int[10];
delete[] array; // 正确的使用方式另一方面当我们使用new分配单个元素时必须使用delete不带[]来删除。如果我们使用delete[]来删除非数组类型的指针那也会导致未定义的行为。
int* single new int();
delete single; // 正确的使用方式 这组规则是相当重要的因为它们涉及到C运行时环境如何在堆上分配和管理内存。遵循这些规则不仅可以避免内存泄漏也能防止程序因为错误的内存释放操作而崩溃。
new和new[]以及delete和delete[]的区别主要在于它们处理的数据结构和内存分配的方式
new和delete这两个操作符主要用于分配和释放单个对象的内存。
int* p new int; // 分配一个整数的内存
delete p; // 释放该整数的内存new[]和delete[]这两个操作符用于分配和释放数组的内存。
int* arr new int[10]; // 分配10个整数的内存
delete[] arr; // 释放这10个整数的内存 对于这两对操作符最重要的一点是必须配对使用。也就是说我们不能使用new来分配内存然后使用delete[]释放内存反之亦然。
注意new和new[]分配的内存不会被自动释放我们需要手动调用相应的delete或者delete[]来释放这些内存否则将导致内存泄漏。这是一个常见的编码错误也是使用RAII资源获取即初始化技术的主要原因它可以通过在对象的生命周期结束时自动释放其拥有的资源从而防止内存泄漏。
17. 以独立语句将newed对象置入智能指针
请记住 以独立语句将newed对象存储于智能指针内。如果不这样做一旦异常被抛出有可能导致难以察觉的资源泄露。
解释 智能指针主要的优点就是它可以保证在任何情况下包括出现异常时都可以正确地释放内存。在C中如果我们创建了一个普通的指针并使用new分配了内存但在释放这个内存前出现了异常那么这个内存就会泄露。
由于异常可能在new之后的任何时间发生所以我们无法确定何时捕获并处理这个异常。因此为了避免内存泄露我们应该尽快将这个指针转换为智能指针。如果我们在创建智能指针对象的同一条语句中使用new那么即使出现异常内存也会安全地释放。
std::unique_ptrint ptr(new int(10)); // 创建一个存储int的智能指针 虽然将new和智能指针的初始化放在同一行更简洁但为了避免new在智能指针接管之前就抛出异常导致的任何可能的资源泄露提倡以独立语句将newed对象存储于智能指针内。如
int* temp new int(10);
std::unique_ptrint ptr(temp); // 创建一个存储int的智能指针 这样即使在new和智能指针的初始化之间抛出异常我们也可以保证delete temp;被调用防止了内存泄漏。