首页 > 其他分享 >Chapter 3

Chapter 3

时间:2022-08-24 02:55:53浏览次数:52  
标签: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,资源,指针
From: https://www.cnblogs.com/Lingh/p/16618437.html

相关文章

  • # Chapter3. 仲裁器专题
    Chapter3.仲裁器专题本专题内容总结自李虹江老师的IC加油站公众号,李老师的讲的内容十分精彩,除了仲裁器还包括异步FIFO、跨时钟域处理,讲的十分透彻,受益匪浅。FixedPri......
  • Chapter8. 单bit信号跨时钟域同步CDC
    Chapter8.单bit信号跨时钟域同步CDC本章导图单bit信号慢到快传输对于电平信号快到慢由于电平信号高电平时间足够长,因此直接打两拍避免亚稳态是可以的。对于边沿信号......
  • Chapter 10 - Archiving (C#实现,实现NSDocument类型窗口的保存和载入)
    这个例子是在Chapter09-NSUndoManager 上继续实现的。所以大家要看前面的例子。xcode布局改变,记住给MainMenu.xib下的Open菜单项添加openDocument:操作关于PersonMo......
  • Chapter 08 - RaiseMan ( C# 实现 + NSDocument类)
    此例子实现了不用ArrayController,基于view-basedtableview实现添加和删除。当然,也可以用ArrayController实现,这样可以省去NSTableViewDelegate和NSTableViewDataSource......
  • Chapter 08 - RaiseMan (C# 实现 + Cell-based tableview绑定)
    这个代码主要展示如何绑定ArrayController到Cell-basedTableview。在发代码前,需要注意几点。1.Person为自定义的class,但是一定要Register为PersonModel。格式{自定义c......