《Effective C++》学习笔记(1~3)
导读
- explicit关键字修饰构造函数,禁止隐式类型转换;
- Widget w1 = w0; //等号语法在此起到调用拷贝构造函数的作用,而不是赋值操作符;
条款01:视C++为一个语言联邦
- 参数传递时,内置类型更适合值传递,用户自定义类型更适合const引用传递;
- C++包含四个语言层次:C、object-oriented C++、template C++、STL;
条款02:尽量以const、enum、inline替换#define
- 宏定义可能导致编译报错时,难以追踪问题。因为宏定义变量在预处理过程中已经被替换为相应的值,在报错时报告替换后的值(如3.14),而不知道该值表达的具体含义使人困惑;
- 宏定义的文本替换会导致更大的代码量;
- 宏定义不重视作用域的限制。无法定义一个class的私有宏变量,不能提供任何封装特性;
define定义的宏函数,参数不会有类型检查,传参中有运算,会被多次运算;
/********宏函数,必须为每个实参加上括号********/ #define MAX(a, b) (a > b ? a : b) //应该为每个参数加上括号; #define MAX((a), (b)) ((a) > (b) ? (a) : (b)) //避免MAX(a + 10, b)在宏替换时被替换为:(a+10 > b ? a+10 : b); //这里就存在多次运算的问题; /********宏函数,为每个实参加上括号,仍存在问题********/ a = 10, b = 10; MAX(++a, b); //宏替换后:((++a) > (b) ? (++a) : (b));这里会导致对a进行两次自增,返回值为12;
enum某些行为有点像define。如不能取地址,不会有不必要的内存分配等;
- 条款小结:
1、对于单纯常量,最好以const对象或enum替换#define;
2、对于形似函数的宏,最好改用inline函数替换#define。
条款03:尽可能使用const
- 对于指针对象: const关键字出现在星号左边,表示被指物为常量;const关键字出现在星号右边,表示指针自身是常量;
函数返回值设置为const,可以有效避免因客户误操作引起的错误;
(a * b) = c; //在(a * b)的基础上调用operator=;本意为做比较,误写为=; //若将op=的返回值设置为const,禁止对返回值进行修改。则编译器就能帮助我们找出错误;
const成员函数:可以让类接口更容易理解。哪些接口可以改变对象内容,哪些接口不能改变对象内容;也可以使得操作“常对象”成为可能(常对象只能调用常成员函数);
- mutable关键字可以释放掉non-static成员变量的bitwise constness约束。使得mutable型的成员变量可以在const成员函数中修改。
- 不要在const成员函数中调用non-const成员函数。避免导致对对象的逻辑状态产生修改(当然,严格的编译器对于这类操作也会报错);
- 当类中需要相同功能的const和non-const函数时,可以在non-const函数中调用const函数,避免代码的重复;
/*****non-const成员函数调用const成员函数的具体用法*****/ class TextBlock { public: …… const char &operator[](int pos) const //常成员函数; { …… return text[pos]; } char &operator[](int pos) //非常成员函数,通过调用常成员函数实现功能; { return const_cast<char &>( static_cast<const TextBlock &>(*this)[pos]); //通过static_cast先将(*this)转换为(const Textblock&),然后调用的const 的op[]=; //最后通过const_cast去掉op[]返回值的const约束; } …… }
- 条款小结:
1、将某些东西声明为const,可以帮助编译器侦测出错误;
2、当const和non-const版本函数具有实质等价的功能时,可在non-const函数中调用const版本函数,避免重复。