类与函数的设计和声明
看看作者思考问题的角度:
对象如何产生和销毁: 构造析构、new和delete的重载
对象的初始化和赋值:构造、拷贝构造、赋值重载
新型别转换相关:合法值的规范检验、继承体系、是否隐式转换
访问权限:public、protected、private如何设计
Item18 -- 努力让接口完满且最小化
1,KISS原则;2,头文件的重要性;
Item19 -- 区分member functions, non-member functions和friend functions三者
1,如果要实现虚函数,必须是member function
2,让operator<<和operator>>成为non-members,如果还需要获取类的非公共成员变量,声明为friend。原因,如果是func为member,那么以后书写顺序应该是obj>>cin,obj<<cout,这样不符合习惯
3,只有non-member才能在最左参数身上实施型别转换。如果需要对函数f的最左侧参数进行型别转换,那么f为non-function,如果还需要获取类的非公共成员变量,声明为frind。
举例,operator *(Class &lhs, Class &rhs)这种声明,2*obj2的调用,需要对2进行型别转换(构造函数声明为explicit可以阻止隐式型别转换),这样就必须为non-member
Item20 -- 避免将data members放在公开接口中
Effective中举了三个原因,说明为什么不要放在公开接口中
- 一致性,以后对类对象的所有操作,均需要带(),也就是只能调用函数,不能获取变量
- 获取控制性,比如只读、可读可写、不处理,通过不同的函数实现
- 函数抽象性,提供一个借口,底层如何实现上层用户不用关心
不过在实际编程中,很少人能够完全做到这点,毕竟需要自己花些时间来写get和set,暂时我也没找到自动生成get、set函数的方法,所以鱼与熊掌不可兼得,若想获得好处,就得费力写get、set了。
Item21 -- 尽可能使用const
1,*号在中间,前定内容后定针
2,返回值用const修饰,说明返回值是只读的,不能修改
3,函数后面用const修饰,说明该函数不能修改任何变量。函数可以据此进行重载,有const的函数被const对象调用,没有const的函数被非const对象调用
const的真正意义是什么?不变性,具体的体现有两种说法:A, bitwise. B, conceptual,A说法对位进行比较,如果没有修改则认为是不变的。B从概念层面进行判断,即使底层有修改,但对上层概念来讲是不变的,那就是不变的。但是C++语言只支持A,所以为了应付B,引入mutable修饰词,用来修饰上层概念不变,但是底层要修改的底层变量。
4,const可以通过const_cast取消常量性
Item22 -- 尽量使用pass-by-reference,少用pass-by-value
- C语言里面都是传值
- 传值成本比较大,会调用对象的拷贝构造,如果类比较复杂,则会创建和析构更多的对象
- 传引用会避免切割问题。Func(base&) 和Func(base)两种函数声明,内部调用f()虚函数,如果传递个derived对象,则传引用会调用derived.f(),而传值则会切割而调用base.f()
Item23 -- 当你必须回传object时,不要尝试传回reference
用重载乘法举例
Inline const Rational Operator*( const Rational& lhs, const Rational & rhs) { return Rational(lhs.n*rhs.n, lhs.d*rhs.d); } |
传回的是value,如果传回reference的话,内部变量析构之后,引用没有真正的对象
Item24 -- 在函数重载和参数缺省化之间,谨慎选择
void g(int x=0); g(); g(10); | void f(); void f(int x); f(); f(10); |
两种方式要谨慎选择,避免出现模棱两可的情况
Item25 -- 避免对指针型别和数值型别进行重载
void f(int x); void f(string *ps); f(0) |
0的存在会对指针和数值造成模棱两可,所以要坚决避免针对指针和数值进行重载
Item26 -- 防卫潜伏的ambiguity(模棱两可)状态
C++有一个哲学信仰,它相信潜在的模棱两可状态不是一种错误,但是对程序员来讲,将所有问题放到运行后发现就是一种灾难。所以程序员应该避免模棱两可。
类的转换,一是拷贝构造方式可以隐式转换,一是operator Class()方式,当需要型别转换时,就会有模棱两可
- 语言标准转换,6.02可以转换成int也可以转换成char
- 多继承也是如此
当遇到模棱两可情况时,程序员应该显式的说明采用哪种方式。
Item27 -- 如果不想使用编译器暗自产生的member functions,就应该明白拒绝它
- 使用private修饰防止公开调用
- 不定义防止friend等调用
private: Array& operator=(const Array &rhs);(注意这里;表示不定义) |
Item28 -- 尝试切割global namespace
namespace name1{
}
using namespace name1;
最好每个人都以自己姓名为name,进行分割,这样可以类似Java中的包的概念