Effective C++ 3nd 读书摘要(三、资源管理 ;四、设计与声明;五、实现)Item13 - 31

三、资源管理
Item13. 以对象管理资源
在构造函数中获得资源并在析构函数中释放资源。如tr1::shared_ptr

 

Item14. 在资源管理类中小心copying行为
抑制copying、施行引用计数法

 

Item15. 在资源管理类中提供对原始资源的访问
一如Item18的忠告,通常显式转换函数如get()、string::c_str()是比较受欢迎的,因为不太可能被误用。

 

Item16. 成对使用new和delete时要采取相同形式
new - delete
new [] - delete []

 

Item17. 以独立语句将newed对象置入智能指针
将processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
这样的语句同样可能出现资源泄漏。因为调用priority、执行new Widget、调用tr1::shared_ptr构造函数,这三者的调用次序是无法确定的(Java与C#则不这样)。

 

所以如果它以这种顺序调用:
执行new Widget、调用priority、调用tr1::shared_ptr构造函数,则万一对priority的调用导致异常,则引发资源泄漏。
所以:
std::tr1::shared_ptr<Widget> pw(new Widget);  // store newed object
                                              // in a smart pointer in a standalone statement
processWidget(pw, priority());           // this call won't leak

 

四、设计与声明
Item18. 让接口容易被正确使用,不易被误用
首先必须考虑客户可能做出什么样的错误:
如一个Date类,接口为Date(int month, int day, int year),可能用户会这样用:
Date d(30, 3, 1995);              // Oops! Should be "3, 30" , not "30, 3"
也可能会这样用:
Date d(2, 20, 1995);              // Oops! Should be "3, 30" , not "2, 20"
但如果将接口改善为Date(const Month& m, const Day& d, const Year& y);
其中Month定义为

这样的接口便不太可能被误用。

保持接口的一致性,与内置类型的行为要兼容。
一旦怀疑,请拿ints作范本。

Item19. 设计class犹如设计type
1.新type的对象应该如何被创建和销毁
2.对象的初始化和对象的赋值应该有什么样的差别
3.新type的对象如果被passed by value,意味着什么
4.什么是新type的“合法值”
5.你的新type需要配合某个inheritance graph吗
6.你的新type需要什么样的转换
7.什么样的操作符和函数对此新type而言是合理的
8.什么样的标准函数应该驳回
9.谁该取用新type的成员
10.什么是新type的“未声明接口”
11.你的新type有多么一般化
12.你真的需要一个新type吗

 

Item20. 宁以pass-by-reference-to-const替换pass-by-value
除了效率问题还可以解决slicing问题。不过不适用于内置类型、STL迭代器和函数对象,对它们应该用pass-

by-value。

 

Item21. 必须返回对象时,别妄想返回其reference
绝不要返回pointer或reference指向local stack对象、heap-allocated对象(尽量以智能指针包裹)、可能多次出现的local static对象

 

Item22. 将成员变量声明为private
protected并不比public更具有封装性

 

Item23. 宁以non-member、not-friend替换member函数
可增加封装性、包裹弹性和机能扩充性

 

Item24. 若所有参数皆需类型转换,请为此采用non-member函数
比如Rational对象与整数进行*运算:
定义:const Rational operator*(const Rational& rhs) const;
result = oneHalf * 2;                             // fine
result = 2 * oneHalf;                             // error!

定义一个non-member函数便可解决这个问题:
const Rational operator*(const Rational& lhs,     // now a non-member
                          const Rational& rhs)     // function

 

Item25. 考虑写出一个不抛异常的swap函数

编译器会查找适当的swap,优先调用T专属的swap,若没有,则使用std::swap。
成员版的swap绝不应该抛出异常

如果你提供一个member swap,也应该提供一个non-member swap来调用前者。对于classes(not templates),请特化std::swap。

五、实现
Item26. 尽可能延后变量定义式的出现时间
只要你定义了一个变量,当程序运行到此时,便得承受构造成本。所以遵循此条款不仅可以增加程序的清晰

性,还可提高效率。

 

Item27. 尽量少做转型动作
尤其在注重效率的代码中。有许多方案可以代替转型。

 

Item28. 避免返回handles指向对象内部成分
可能导致dangling handles(包括references、指针、迭代器)。

 

Item29. 为“异常安全”而努力是值得的
当异常被抛出时,带有异常安全性的函数会:
1.不泄漏任何资源
2.不允许数据败坏

 

异常安全函数提供以下三个保证之一:(如果异常被抛出,则)
1.基本承诺:没有任何对象或数据结构会因此而败坏,所有对象都处于一种内部前后一致的状态
2.强烈保证:程序状态不改变,如果函数失败,程序会回复到“调用函数之前”的状态(往往能够以copy-and-swap实现出来)
3.nothrow保证:int doSomething() throw();  并非说doSomething绝不会抛出异常,而是说若抛出异常将是严重错误,系统将调用由set_unexpected指定的函数。

 

为此在写或改代码时,首先是“是对象管理资源”,然后挑选三个“异常安全保证”中的某一个实施于你所写的每一个函数身上。应该挑选“现实可操作”条件下的最强烈等级。

 

Item30. 透彻了解inlining的里里外外
1.如果inline函数的本体很小,将使object code增加,从而增加内存占用,并减小cache的击中率。
2.inline函数对所有的virtual函数都不起作用。构造函数和析构函数通常不应该作为inline函数。
3.inline函数无法随着程序库的升级而升级,它需要所有用户到此inlining函数的客户端程序都重新编译,而如

果是个non-inline函数,则只需要重新连接就好。

 

一个合乎逻辑的策略是:一开始不要将任何函数声明为inline,日后使用80-20原则来对那些小型的、被频繁

调用的函数进行inline。

 

Item31. 将文件间的编译依存关系降到最低
针对Person,可以将Person分割为两个classes,一个只提供接口,另一个负责实现此接口。

这种设计常被称为pimpl idiom (pointer to implemenatation)。这样的类称为handle class。(另一种便是interface class,由pure virtual class构成)

 

handle class和interface class解除了接口和实现之间的耦合关系,从而降低了文件间的编译依存性。

 

这种分离体现了编译依存性最小化的本质:现实中让头文件尽可能自我满足,万一做不到,则让它与其他文

件内的声明式(而非定义式)相依存。


具体如下:
1.如果使用object references或object pointers可以完成任务,就不要使用objects。
2.如果可能,尽量以class声明式替换class定义式。
3.为声明式和定义式提供不同的头文件。如C++标准程序库头文件的<iosfwd>(fwd=forward)中包含为

iostream各组件的声明式:


// char TYPEDEFS
typedef basic_ios<char, char_traits<char> > ios;
typedef basic_streambuf<char, char_traits<char> > streambuf;
typedef basic_istream<char, char_traits<char> > istream;
typedef basic_ostream<char, char_traits<char> > ostream;
typedef basic_iostream<char, char_traits<char> > iostream;
......
,而其对应定义则分布在若干不同的头文件内,包括<iostream>,<streambuf>...

 

但handle class和interface class是要付出性能代价的。应该以渐进方式使用这些技术(如同上一个Item),首先使用handle class和interface class,而当它们导致性能问题时再用concrete classes替换它们。

 

程序库头文件应该以“完全且仅有声明式”的形式存在,不论是否涉及templates。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值