条款13 以对象管理资源

  • RAII对象

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

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

复制底层资源:复制资源管理的对象 应该同时也复制其所包覆的资源 复制资源管理对象时进行的是深拷贝

普遍而常见的RAII行为是:阻止拷贝、运用引用计数法。 不过其他行为也都可能被实现。

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

智能指针提供get成员函数 用来返回智能指针内部的原始指针(的副本);智能指针也重载了指针取值操作符(operator->operator *),允许隐式转化至底部原始指针

如果觉得每一次都需要调用get非常麻烦 可以令为资源管理类提供隐式转换函数operator type

  1. class Font{
  2. public:
  3. ...
  4. operator FontHandle()const {return f;}
  5. };

但是隐式类型转换可能会引发问题

  1. Font f1(getFont());
  2. ...
  3. FontHandle f2=f1;//本意是拷贝一个Font对象 但是隐式转换为FontHandle才复制它

f1和f2都管理一个FontHandle,如果f1被销毁 那么f2因此称为“虚吊的”。

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

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

当使用new时,首先内存会通过operator new分配出来,然后针对此内存会调用一个或多个构造函数;

当使用delete时,首先针对此内存会调用一个或多个析构函数,然后内存才会通过oeprator delete释放。所以delete的最大问题在于:即将被删除的内存之内究竟有多少对象?这决定了有多少个析构函数被调用

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

**shared_ptr**需要原始指针的构造函数是**explicit**,无法进行隐式类型转换,将得自new Widget的原始指针转换为process所要求的shared_ptr

image.pngimage.png
二者 一个能通过编译,一个不能通过编译。说明单一参数的构造函数提供了一个隐式类型转换的方式(将传参类型转换为类类型),如果构造函数声明为**explicit**那么这种转换将不复存在

HINT:cppreference写明,一个只有一个参数且没有被声明为explicit的构造函数称为转换构造函数,而且这种构造不同于explicit的构造函数,这种构造也会应用于拷贝初始化。它提供了隐式的类型转换


  1. //函数声明
  2. int priority();
  3. void process(std::shared_ptr<Widget>pw,int priority);
  4. process(new Widget,priority()); //无法通过编译
  5. process(std::shared_ptr<Widget>((new Widget),priority())//可以通过 但可能泄漏资源

C++编译器核算函数参数的顺序是不固定的!

如果按照如下顺序:
①执行 new Widget
②调用priority
③调用std::shared_ptr构造函数

如果对priority的调用产生异常,new Widget返回的指针将会遗失,因为尚未被置入std::shared_ptr内


解决方法:使用分离语句构造智能指针

  1. std::share_ptr<Widget>pw(new Widget);
  2. process(pw,priority()); //大功告成!

编译器对于“跨越语句的各项操作”没有重新排列的自由 。只有在一条语句内部才有重排的自由度。