EC读书笔记系列之12:条款22、23、24
条款22 将成员变量声明为private
记住:
★切记将成员变量声明为private。这可赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并提供class作者以充分的实现弹性。
★protected并不比public更具封装性
条款23 宁以non-member-non-friend替换member函数
记住:
★宁可拿non-member-non-friend函数替换member函数。这样可增加封装性、包裹弹性和机能扩充性。
-------------------------------------------------------------------------------
举例:
class WebBrowser {
...
void clearCache(); //清除缓存
void clearHistory();//清除历史
void clearCookies();//清除cookies
...
};
假设想一次性执行上面三个有关清理的函数,有两种方法:
方法一:将这个一次性清理工作函数写成member function:
class WebBrowser {
public:
...
void clearEverything(); //调用上面的三个函数
...
};
方法二:写成non-member-non-friend函数:
void clearBrowser( WebBrowser &wb ) {
wb.clearCache();
wb.clearHistory();
wb.clearCookies();
}
方法二要好一点!!!因为non-member函数将导致较大的封装性,∵其并不能访问class内部的private成分。(因为对于对象内的数据来说,愈少代码可以看到数据,也即访问它,愈多的数据就可被封装,而我们也就愈能自由地改变对象的数据,这不也就是封装性的体现吗!!)
两件需要注意的事:
一、上述论述仅适用于no-member-non-friend函数;
二、因在意封装性而让函数“成为class的non-member”,并不意味着其“不可以是另一个class的member”
推荐做法:
让clearBrowser成为一个non-member并且位于WebBrowser所在的同一个namespace内:
namespace WebBrowserStuff {
class WebBrowser {...};
void clearBrowser( WebBrowser &wb );
...
} //注意namespace这里无分号!!!
注:像上面WebBrowser这样的class可能拥有多种便利函数,如某些与书签有关,某些与打印有关等。可以将这些便利函数的编译相依关系降低:分离他们的直接做法是将书签相关的便利函数声明于一个头文件,将打印相关的便利函数声明于另一个头文件:
namespace WebBrowserStuff {
class WebBrowser{...}; //核心机能
}
//头文件"webbrowserbookmark.h"
namespace WebBrowserStuff {
... //与书签相关的便利函数
}
//头文件"webbrowserprint.h"
namespace WebBrowserStuff {
... //与打印相关的便利函数
}
以此种方式切割机能并不适用于member函数,因为一个class必须整体定义,不能被分割为片段,从这个角度来说也符合本条款的主题。
条款24 若所有参数皆需类型转换,请为此采用non-member函数
记住:
★若需要为某个函数的所有参数(包括被this指针所指的那个隐喻参数)进行类型转换,那么此函数必须是个non-member。
-------------------------------------------------------------------------------
举例:有理数类
class Rational {
public:
Rational( int numerator = ,
int denominator =
); //构造函数刻意不为explicit,
//这样做是为了int-to-Rational隐式转换
...
};
现让该有理数类支持乘法运算。现将operator*写为类的member函数:
class Rational {
public:
...
const Rational operator*( const Rational &rhs ) const;
};
若下面这样用:
Rational oneEighth( , );
Rational oneHalf( , );
Rational result;
result = oneHalf*oneEighth; //用法1:可以
result = oneHalf*; //用法2:可以
result = *oneHalf; //用法3:错误
这里的用法2,之所以行得通,是因为这里发生了隐式类型转换,等价于下面这样调用:
const Rational temp(2); //建立一个暂时性的Rational对象
result = oneHalf*temp; //等价于oneHalf.operator*( temp );
当然这种做法可行的前提是non-explicit constructor!!!
仅当参数被列于参数列内,此参数才是隐式类型转换的合格参与者,这是用法2和用法3的区别。
解决方案:将operator*写成non-member函数,这样便可以使在每一个实参身上执行隐式类型转换!!!
const Rational operator*( const Rational &lhs,
const Rational &rhs
) { return Rational( lhs.numerator() * rhs.numerator(),
rhs.denominator() * rhs.denominator()
);
}
这样:
result = oneHalf*;
result = *oneHalf; //也可以通过编译了!!!
注意:
member函数的反面是non-member函数,而不是friend函数!!!(因为有太多的C++程序员错误地认为若一个“与某class相关”的函数不该成为一个member,就该是个friend)
EC读书笔记系列之12:条款22、23、24的更多相关文章
- EC读书笔记系列之7:条款12 复制对象时勿忘其每一个成分
记住: ★copying函数应确保复制“对象内的所有成员变量”及“所有base class成分” ★不要尝试以某个copying函数实现另一个copying函数.应该将共同机能放进第三个函数中,并由两 ...
- EC读书笔记系列之16:条款35、36、37、38、39、40
条款35 考虑virtual函数以外的其他选择 记住: ★virtual函数的替代方案包括NVI手法及Strategy模式的多种形式.NVI手法自身是一个特殊形式的Template Method模式 ...
- EC读书笔记系列之1:条款1、条款2、条款3
条款1:视C++为一个语言联邦 记住: ★C++高效编程守则视状况而变化,这取决于你使用C++的哪一部分 C: Object-oriented c++: Template c++: STL 条款2:尽 ...
- EC读书笔记系列之20:条款53、54、55
条款53 不要轻忽编译器的警告 记住: ★严肃对待编译器发出的警告信息.努力在你的编译器的最高(最严苛)警告级别下争取“无任何警告”的荣誉 ★不要过度依赖编译器的报警能力,∵不同的编译器对待事情的态度 ...
- EC读书笔记系列之19:条款49、50、51、52
条款49 了解new-handler的行为 记住: ★set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用 ★Nothrow new是一个颇为局限的工具,∵其只适用于内存 ...
- EC读书笔记系列之18:条款47、48
条款47 请使用traits classes表现类型信息 记住: ★Traits classes使得“类型相关信息”在编译期可用.它们以templates和“templates特化”完成实现 ★整合重 ...
- EC读书笔记系列之17:条款41、42、43、44、45、46
条款41 了解隐式接口与编译器多态 记住: ★classes和templates都支持接口和多态 ★对classes而言接口是显式的(explicit),以函数签名为中心.多态则是通过virtual函 ...
- EC读书笔记系列之15:条款32、33、34
条款32 确保你的public继承塑模出is-a关系 记住: ★public继承意味着is-a.适用于base class身上的每一件事情一定也适用于derived class身上,∵每一个deriv ...
- EC读书笔记系列之14:条款26、27、28、29、30、31
条款26 尽可能延后变量定义式的出现时间(Lazy evaluation) 记住: ★尽可能延后变量定义式的出现.这样做可增加程序的清晰度并改善程序效率 ----------------------- ...
随机推荐
- BZOJ 1833 ZJOI2010 count 数字计数 数位DP
题目大意:求[a,b]间全部的整数中0~9每一个数字出现了几次 令f[i]为i位数(算前导零)中每一个数出现的次数(一定是同样的,所以仅仅记录一个即可了) 有f[i]=f[i-1]*10+10^(i- ...
- javascript中关于数组的迭代方法
//都接受3个参数,分别为:值.在数组中的位置.数组对象本身 var num = [2, 1, 5, 4, 2, 1, 6, 8, 19]; //every:若每一项都返回true,则返回true v ...
- C#核编之格式化编程
一.格式化控制台输入输出 1. 在前面的随笔中,会经常看到诸如{0},{1}之类的标记嵌入在字符串变量中..NET引入一种字符串格式化的新风格.与C的printf()相似,简而言之,如果需要定义一个字 ...
- android stuido 快捷键
Alt+回车 导入包,自动修正 Ctrl+N 查找类 Ctrl+Shift+N 查找文件 Ctrl+Alt+L 格式化代码 Ctrl+Alt+O 优化导入的类和包 Alt+Insert 生成代码 ...
- Git 详细命令集
初始化一个Git仓库,使用git init命令. 添加文件到Git仓库,分两步: 第一步,使用命令git add <file>,注意,可反复多次使用,添加多个文件: 第二步,使用命令git ...
- Android Material Design之Toolbar与Palette
转:http://blog.csdn.net/jdsjlzx/article/details/41441083 前言 我们都知道Marterial Design是Google推出的全新UI设计规范,如 ...
- CSS 3 属性学习大纲
1. Gradient 渐变 2. RGBA 颜色透明 3. Border-radius 圆角 4. text-shadow 文字阴影 5. box-shadow 图层阴影 6. Transform ...
- PHP 源码加密扩展(php-beast)PHP7 版本发布
此版本主要支持PHP7,在github(https://github.com/liexusong/php-beast)上选择php7分支然后编译安装即可. 来源于:https://github.c ...
- jQuery学习-事件之绑定事件(二)
在上一篇<jQuery学习-事件之绑定事件(一)>我们了解了jQuery的add方法,今天我们来学习下dispatch方法: dispatch: function( event ) { ...
- js 概念(构造函数)
所有关于类.对象的语言里面,都有构造函数的概念,其实构造函数,就是在创建这个对象或者类的实例时候自动调用的函数,一般的语言都是new创建,那么new的参数就传递给构造函数.