目录
前几章的条记多有不敷,这一章会持续改进
条款13:以对象管理资源(Use objects to manage resources)
关键想法
考虑以下易出错的例子:- class Investment { ... }; //投资类型继承体系中的root类
- //工厂函数,指向Investment继承体系内的动态分配对象,参数省略
- Investment* createInvestment {};
- void f()
- {
- Investment* pInv = createInvestment(); //调用工厂函数
- ... //若这里return则无法执行delete
- delete pInv; //释放pInv所指对象
- }
复制代码 办理方案:把资源放进对象,可利用析构函数自动调用机制确保资源释放
以对象管理资源的两个关键想法:
- 获得资源后立刻放进管理对象(managing object)内
- 资源取得机会便是初始化机会(Resource Acquisition Is Initialization,RAII)
- 偶然获得的资源会拿来赋值而非初始化
- 管理对象运用析构函数确保资源释放
- 岂论控制流怎样离开区块,一旦对象被烧毁(如离开对象作用域)其析构函数会自动调用
智能指针
auto_ptr:
- 通过copy构造函数或copy assignment操纵符复制它们,它们会变成null,复制所得的指针将取得资源的唯一全部权
- 故需要元素能够复制地STL容器不兼容auto_ptr
auto_ptr在C++11中已被弃用,以下简要介绍
- void f()
- {
- std::auto_ptr<Investment> pInv(createInvestment());
- ...
- } //auto_ptr的析构函数自动删除pInv
- std::auto_ptr<Investment< pInv1(createInvestment());
- std::auto_ptr<Investment< pInv2(pInv1); //现在pInv2指向对象,pInv1为null
- pInv1 = pInv2; //现在pInv1指向对象,pInv2为null
复制代码 优缺点
偶然必须取得RAII对象内的原始资源,考虑用于字体的RAII类:- void f()
- {
- std::tr1::shared_ptr<Investment> pInv(createInvestment());
- ...
- } //shared_ptr的析构函数自动删除pInv
- void f()
- {
- ...
- std::tr1::shared_ptr<Investment> pInv1(createInvestment());
- std::tr1::shared_ptr<Investment> pInv2(pInv1); //指向同一个对象
- pInv1 = pInv2; //同上
- ...
- } //pInv1和pInv2被销毁,他们所指的对象也被销毁
复制代码
- 表现转换:可读性强,但是需要API时必须调用get
- std::auto_ptr<std::string> aps(new std::string[10]); //会调用错误形式的delete
- std::tr1::shared_ptr<int> spi(new int[1024]); //同上
复制代码
- 隐式转换:调用C API更自然,但是易出错
- 可能在需要Font时不测创建FontHandle,且f被烧毁则f0成为悬空的(dangle)
- void lock(Mutex* pm); //锁定pm所指的互斥器
- void unlock(Mutex* pm); //解除互斥器的锁定
- //管理机锁的类,符合RAII守则
- class Lock {
- public:
- explicit Lock(Mutex* pm)
- : mutexPrt(pm)
- { lock(mutexPtr); }
- ~Lock() { unlock(mutexPtr); }
- private:
- Mutex *mutexPtr;
- };
- //客户对Lock的用法符合RAII方式
- Mutex m;
- ...
- {
- Lock ml(&m);
- ...
- }
复制代码 Tips:
- API往往要求访问原始资源,故RAII类应提供取得其管理的资源的方法
- 对原始资源的访问包含表现转换和隐式转换,一般表现转换安全而隐式转换方便
条款16:成对使用new和delete时要采取相同形式(Use the same form in corresponding uses of new and delete)
- Lock m11(&m); //锁定m
- Lock m12(m11); //将m11复制到m12上
复制代码 以上程序的举动不明确:
- 使用new时会发生两件事:
- delete需要知道被删除的内存内有多少对象,其决定了要调用多少析构函数
- 若指针指向数组对象,则数组所用的内存包含数组大小信息
- 若指针指向单一对象,则无上述信息
- 需要人为告诉delete所删除的对象类型
- class Lock {
- public:
- explicit Lock(Mutex* pm) //以Mutex初始化shared_ptr
- : mutexPtr(pm, unlock) //以unlock函数作为删除器
- {
- lock(mutexPtr.get());
- }
- private:
- std::tr1::shared_ptr<Mutex> mutexPtr; //使用shared_ptr替换raw pointer
- };
复制代码 使用typedef需要考虑相同的问题(下例中数组使用typedef并不合适,容易产生错误,仅做说明):- class Investment {
- public:
- bool isTaxFree() const;
- ...
- }
- Investment* createInvestment(); //工厂函数
- std::tr1::shared_ptr<Investment> pInv(createInvestment());
- int daysHeld(const Investment* pi); //返回投资天数
- int days = daysHeld(pInv); //错误!daysHeld需要Investment*而非tr1::shared_ptr
- //显示转换
- int days = daysHeld(pInv.get());
- //隐式转换,tr1::share_ptr重载了指针取值(pointer dereferencing)操作符(->和*)
- bool taxable1 = !(pInv->isTaxFree());
- bool taxable2 = !((*pInv).isTaxFree());
复制代码 Tips:
- 如果new加上[],则delete必须加上[];如果new没加[],则delete不能加[]
条款17:以独立语句奖newed对象置入智能指针(Store newed objects in smart pointers in standalone statements)
考虑涉及优先权的例子:- FontHandle getFont(); //这是C API,省略参数
- void releaseFont(FontHandle fh); //来自同一组
- class Font { //RAII类
- public:
- explicit Font(FontHandle fh) //获得资源
- : f(fh) //使用pass-by-value,因为C API这样做
- { }
- ~Font() { releaseFont(f); } //释放资源
- private:
- FontHandle f; //原始字体资源
- };
复制代码 编译器产出processWidget调用码之前,必须首先核酸即将被传递的各个实参,其要办事情有三件:
- 第一实参
- 执行new Widget
- 调用tr1::shared_ptr构造函数
- 第二实参
现实执行的序次弹性很大,只能确定执行new Widget肯定先于调用tr1::shared_ptr构造函数,但调用priority的序次不愿定。若编译器选择以下序次:
- 执行new Widget
- 调用priority
- 调用tr1::shared_ptr构造函数
则如果调用priority出现异常,那new Widget返回的指针将遗失,其将来得及放入tr1::shared_ptr内,进而导致资源走漏。即创建资源和资源转换为资源管理对象直接有可能发生异常干扰
办理方法:使用分离语句,因为编译器对于跨越语句的各项操纵没有重新排列的自由- class Font {
- public:
- ...
- FontHandle get() const { return f; } //显式转换函数
- ...
- };
- void changeFontSIze(FontHandle f, int newSize);
- Font f(getFont());
- int newFontSize;
- ...
- changeFontSize(f.get(), newFontSize);
复制代码 Tips:
- 以独立语句将newed对象存储于智能指针内,否则一旦有异常则可能发生隐秘的资源走漏
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |