effective c++ Item26~31 实现-学习整理
概括:
Item 18 , Item 19 从大的轮廓综述类型设计上应该注意的地方。
Item 20,Item 21 函数的签名设计应该注意的地方(参数尽量传const to reference、谨慎返回对象的reference)。
Item 22 告诫我们别把数据成员乱放,都放private里。
Item 23,Item 24 告诫我们2种看似要用成员函数实现,其实应该用非成员函数实现的场合。这两种场合我们应该设计成类的非成员函数。
详细来看:
Item 26 让接口容易被使用,不易被误用
重点:
(1)“促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容
(2)“阻止误用”的办法包括建立新类型、束缚对象值(第一个Date()的例子);限制类型上的操作(例如加上const);消除客户的资源管理责任,不要要求客户去做某些事情(引用Item13 返回Investment* createInvestment()的例子)
(3)std::shared_ptr支持定制删除器,可以防范DLL问题,可被用来自动接触互斥锁。
Item 27 尽量少做转型动作
c++转型有6种方式,1种c风格的转型动作、1种函数风格的转型动作和4种c++新式转型
(T)expression
T(expression)
const_cast<T>(expression)
dynamic_cast<T>(expression)
reinterpret_cast<T>(expression)
static_cast<T>(expression)
4种新式含义不同:
const_cast<T>(expression) 通常被用来将对象的常量属性转除。唯一有此能力的c++-style转型操作符
dynamic_cast<T>(expression) 主要用来执行“安全向下转型”。唯一无法由旧式语法执行的动作
reinterpret_cast<T>(expression) 意图执行低级转型
static_cast<T>(expression) 用来强迫隐式转换
建议:
(1)尽量避免dynamic_casts,如果有个设计需要转型动作,试着发展无需转型的替代设计(base,derived体系里,base里声明“什么也不做”的virtual函数,或者用derived对象的容器)
(2)代码里用c++新4式替代旧式执行转换。
Item 28 避免返回handles指向对象内部成分
Item 21 必须返回对象时,别妄想返回其引用
这里作者举了3个例子。
第一个是代码块里的局部变量,引用出错,指向不存在的东西;
第二个是非得这么搞之第一种姿势:代码块里用new在堆上给分配内存的对象,虽然能返回,但是存在两个问题:
(问题1)这个heap-based对象谁来删除?
(问题2)导致内存泄漏
然后这么玩
第三个是非得这么搞之第二种姿势:函数体内部用static Rational 对象,返回这个对象的引用。
存在问题:todo
Item 22 将成员变量声明为private
作者从两方面解释了为什么要将数据成员变量声明为private
(原因一)保持一致性(Item18,让接口容易被使用,不易被误用)
(原因二)封装。将数据成员隐藏在功能性接口之后能为各种实现提供弹性,预留了以后作者改变实现决策的权力。放在public里的话,未来再想改变任何一个public的东西的能力就非常有限了,因为有太多的客户代码将被破坏。
另外,作者也分析了把成员变量放在protected里行不行?答案同样是不行。因为protected声明的数据成员,虽然对直接使用该类的客户来说是不可见的,但是在派生的场合,这些protected声明的数据和public一样,如果派生类的作者和之前的客户一样,使用了这些数据,那么改变这个基类(比如基类的作者消除了这个protected声明的数据成员),那对于派生类的作者来说简直就是灾难。
所以,只要是数据成员,就一定声明为private。
Item 23 宁以non-member、non-friend替换member函数
先举个例子,再说说三点好处。考虑WebBrowser这个类,提供clearCache,clearHistory,removeCookies功能函数:
用户希望一起执行上述3个动作,可以写出如下函数去做
函数一:写一个新的成员函数clearEverything()
函数二:写一个非成员函数clearBrowser(webBrowser &wb)
好处一:c++面向对象原则指出数据应该尽可能被封装,所以上述两种函数,写法二的封装性更好。
这里作者也对比了友元函数,不建议将clearBrowser做成友元函数,理由同样是和成员函数一样破坏封装性。另外Item 24 在隐式转换的观点出发,同样在成员和非成员函数之间做了选择。
好处二:提供非成员函数允许WebBrowser相关功能的更大的包装弹性。
好处三:提高技能扩充性。
我们通常可以将clearBrowser与WebBrowser放在同一个namespace中,成为该namespace的一个非成员函数。
将所有方便性函数放入多个头文件中,但是在一个namespace中。意味着客户能容易地扩充方便性函数集合。
Item 24 当所有参数都需要类型转换,请为此采用non-member函数
这个建议主要来自于下边这个例子:数据类一半都要求满足一些算术运算律,比如交换律。这些类在设计时就应当考虑如何实现这个运算律功能——最直观的方式就是使用成员函数!但是现实中,我们还要考虑混合式算术,即此交换律还应满足混合式,但是混合式的支持,用成员函数的方式就出现了下边的问题,2在前时无法通过编译,原因就在于2无法隐式转换为一个Rational对象,编译器不这么干!(注意隐式类型转换,因此不要将构造函数声明为explicit)
所以我们不得不将此运算律的成员函数实现方式转换为非成员函数的方式实现,让这两个可能需要类型转换的操作数都位于参数列表中。