ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Chapter 3

2022-08-24 03:00:57  阅读:166  来源: 互联网

标签:Chapter RAII shared unique ptr 资源 指针


资源管理

条款 13 以对象管理资源

​ “以对象管理资源“”也被称为“资源取得时机便是初始化时机(RAII)”。

  • 获得资源后立即放进管理对象内,即在构造函数中获取资源。
  • 管理对象(managing object)运用析构函数确保资源被释放。

​ 在C++11中,应该使用 shared_ptrunique_ptr 来管理指针,因为C++的RAII机制会在两者的析构函数中自动 delete 掉指针。

​ 两者区别 :

  • shared_ptr 内部管理一个“被引用计数器”,用来指示有多少个 shared_ptr 管理同一指针,当“被引用计数器“的值变为 0 时,该指针被释放。
  • unique_ptr 只能管理一个指针,多个 unique_ptr 不能同时管理一个指针,因为其内部是禁止复制的。

​ 注意 :

  • 智能指针析构函数执行 delete 而不是 delete[] 动作,因此不要把动态分配数组交给智能指针管理,虽然能通过编译。
  • C++中没有“动态分配数组”的设计,C++17后支持,也可以使用boost的 boost::scoped_arrayboost::shared_array

*请记住 : *

1. 为防止资源泄漏,请使用RAII对象,它们在构造函数中获取资源并在析构函数中释放资源。

2. 两个被使用的 RAII classes 分别是 tr1::shared_ptrtr1::unique_ptr,前者是较佳选择,因为其 copy 行为比较直观。后置被禁止赋值。

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

​ 当你自定义 RAII 对象时,如果只是进行简单的赋值,会出现多个RAII对象管理同一资源,当这些对象调用析构函数时,这个资源将会被多次释放,产生错误。

​ 因此"当一个 RAII 对象被复制"应当 :

  • 禁止复制,例如unique_ptr
  • 对底层资源祭出“引用计数法”,当对象出现复制行为时,“被引用次数”递增,例如 shared_ptr

​ 通常我们会在 RAII classes内含一个 shared_ptr 成员变量,把原来的指针交给智能指针管理即可,如果我们有时候在资源释放时不仅仅是 delete 掉它,还要进行其他操作,比方说互斥器的解锁,那么可以使用指针的“删除器“(函数指针或函数对象)。

​ 例

class Lock{
public:
    explicit Lock(Mutex *pm) : mutexPtr(pm, unlock)//unlock为函数指针或函数对象
    {
        lock(mutexPtr.get());
    }
private:
    std::shared_ptr<Mutex> mutexPtr;
};
  • 复制底部资源,也就是所谓的“深度拷贝”,即复制 RAII 对象时,并不是复制其管理的指针,而是复制该指针指向的“底部资源”,这样就可以防止同一资源被释放多次了。注意默认的复制函数是不会进行“深度拷贝”的。
  • 转移底部资源的拥有权,也就是 auto_ptr,但是在 C++11中已经被弃用,可以使用 unique_ptr代替。
//使用右值引用转移unique_ptr的所有权
std::unique_ptr<Test> t_ptr1(new Test);
std::unique_ptr<Test> t_ptr1 = std::move(t_ptr1);

*请记住 : *

1. 复制 RAII 对象必须复制它所管理的资源,所以资源的 copying 行为决定 RAII 对象的 copying 行为。

2. 普遍常见的 RAII class copying 行为是 : 抑制 copying 、施行引用技术法。不过其他行为也可能被实现,例如深度拷贝和所有权转移。

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

​ 当你需要使用 C APIs 的时候,RAII 对象往往需要向用户提供原始资源(即所管理的指针)。

​ 有两种方式 :

  • 显式转换

  • 隐式转换

class Font{
public:
    //显式转换
    FontPtr get()
    {
        return f;
    }
    
    //隐式转换
    operator FontPtr() const
    {
        return f;
    }
private:
    FontPtr f;
}
void changeFontSize(FontPtr f, int newSize);

//.c
Font f;
changeFontSize(f,10);//隐式转换,调用f.operator FontPtr()
changeFontSize(f.get(),20);//显式转换

​ 虽然这样做容易带来资源泄漏的问题,但当你确实需要的时候也是可以这样做的。

*请记住 : *

1. APIs 往往要求访问原始资源,所以每一个 RAII class 应该提供一个“取得其所管理的资源”的方法。

2. 对原始资源的访问可能经由显式转换或隐式转换,一般而言显式转换比较安全,但隐式转换对客户比较方便。

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

​ 规则 :

  1. 调用 new 时使用 [],对应的调用 delete 时也要使用 []。
  2. 最好不要对数组形式做 typedefusing之类的动作。

*请记住 : *

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

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

​ 举个例子 :

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

//调用
processWidget(std::shared_ptr<Widget>(new Widget),priority());

调用 processWidge() 时其参数内的语句执行顺序弹性很大,没有确定的执行顺序。

假设 :

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

若第二步抛出异常,指针将会遗失,导致资源泄漏。

**解决方法 : **使用分离语句。

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

*请记住 : *

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

标签:Chapter,RAII,shared,unique,ptr,资源,指针
来源: https://www.cnblogs.com/Lingh/p/16618437.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有