01 View C++ as a federation of languages
C++四大组成部分:
- C;
- Object-Oriented C++;
- Template C++;
- STL;
02 Perfer consts, enum, and inlines to #defines
class
内常量已在声明时设置初值,则在定义时不可以再设初值;#defines
不重视作用域,不能够用来定义class
专属常量,也不能提供封装性;- 枚举类型的数值可充当
ints
被使用(the enum hack
);
class GamePlayer{
private:
enum{NumTurns=5};
int scores[NumTurns];
...
};
- 定义宏时,必须为宏中的所有实参加上小括号,避免调用出现问题,但仍有风险;
#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b))
风险示例:
int a=5, b=0;
CALL_WITH_MAX(++a, b); //a被累加两次
CALL_WITH_MAX(++a, b+10); //a被累加一次
- 对于单纯常量,最好用
const
对象或enums
替换#defines
; - 对于形似函数的宏(
macros
),最好改用inline
函数替换#defines
;
template<typename T>
inline void callWithMax(const T& a, const T& b){
f(a > b ? a : b)
}
03 Use const whenever possible
STL
迭代器是以指针为根据操作的,迭代器的作用相当于一个T*
指针;声明迭代器为const
相当于声明指针为const
,即声明一个T* const
指针,是一个指针常量;const_iterator
相当于const T*
指针,是一个常量指针,指向的内容不可改变;
std::vector<int> vec;
...
const std::vector<int>::iterator iter = vec.begin(); //iter相当于一个T* const
*iter = 10; //可以,所指向的内容可以改变
++iter; //错误,指针常量不可改变
std::vector<int>::const_iterator cIter = vec.begin(); //cIter相当于一个const T*
*cIter = 10; //错误,常量指针,所指向的内容是常量
++cIter; //可以,指针变量可以改变
- const 修饰成员变量
(1) 如果const位于*左侧,表示指针所指数据是常量,不能修改该数据;指针本身是变量,可以指向其他的内存单元;
(2) 如果const位于*右侧,表示指针本身是常量,不能指向其他内存地址;指针所指的数据可以修改;
(3) 两个const,*左右各一个,表示指针和指针所指数据都不能修改; const修饰函数参数
(1) 如果输入的参数采用“指针传递”,那么加上const可以防止意外的改动该指针,起到保护作用,例如:void func(char *dest_str, const char *src_str)
;
(2) 如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就不需要保护,所以不需要加const,例如:void func(int x)
不需要写成void func(const int x)
;
(3) 但是对于非内部的数据类型的参数而言,例如void func( A a)
这样声明的函数注定效率低,因为函数体内将自动产生临时对象,用于复制该参数a,而临时变量的构造,拷贝构造,析构都将耗费时间。所以为了提高效率可以写成这样void func(A &a)
用“引用传递”就不用产生临时对象,如果不想改变参数a就写成void func(const A &a)
;const修饰成员函数
(1) const成员函数内不能修改任何的成员变量(mutable修饰的变量除外);
(2) const成员函数不能调用非const成员函数,因为非const成员函数可以会修改成员变量;(注意const成员函数的声明是将const放到函数的尾部,例如:int func(void)const{}
)const修饰函数返回值
(1) 使得函数调用表达式不能作为左值。用const来修饰返回的指针或引用,保护指针指向的内容或引用的内容不被修改,常用于运算符重载;
(2) 以“指针传递”方式的函数返回值加上const修饰,那么该函数的返回值的内容不能被修改,例如:const char *get_string(void)
(3) 如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部的临时存储单元中,所以加上const修饰没有意义,例如:int get_number(void)
不需要写成const int get_number(void)
;
(4) 如果返回值不是内部数据类型,例如:A get_string(void)
,这样会产生一个临时的对象用来存放返回的数据,会调用拷贝构造函数,这样效率会低,可以采用“引用传递”A &get_string(void)
,如果加上const那么返回值的内容就不会被改变const A &get_string(void)
;
04 Make sure that objects are initialized before they’re used
- 永远在使用对象之前先将它初始化;
- 确保每一个构造函数都将对象的每一个成员初始化;
- C++中,对象的成员变量的初始化动作发生在进入构造函数本体之前;
- 赋值(assignment)与初始化(initialization)的区别:
//赋值
class AB{
public:
AB(const std::string& name, const std::string& address);
private:
std::string theName;
std::string theAddress;
};
AB::AB(const std::string& name, const std::string& address){
theName = name;
theAddress = address;
}
//初始化(成员初值列member initialization list)
AB::AB(const std::string& name, const std::string& address):theName(name), theAddress(address){}