Effective C++是一本非常好的进阶书,里面很多思想是平时大家很难或者很不容易去思考或想明白的,在找工作之前,也翻阅了两遍此书,并偶尔留下了些许笔记,今天贴到这里,以供参考。
1、强制类型转换
static_cast:完成相关类型之间的转换,编译器能对static_cast做一些小的类型检查,运行时不检查;
reinterpret_cast:处理互不相关的类型之间的转换,很危险;
dynamic_cast:运行中检查的转换形式;
const_cast:去除const和volatile限定符的转换形式;
2、静态变量
局部变量将在运行线程到达其定义的时候进行初始化,并且会在每个函数调用中产生一份局部变量的副本。而如果一个局部变量被声明为static,那么将只有唯一的一个静态分配对象,用于该函数的所有调用中,这个静态变量将在执行线程第一次到达其定义时进行初始化。——
初始化一次,一直存在,可以为函数提供一种“存储器”,使我们不必引入可能被其他函数访问或者破坏的全局变量。(只能在函数内部访问)
3、const& 与non_const&参数传递
修改引用参数的函数将会使程序很难阅读,因此,在传递引用参数时,除非明确要修改引用参数,否则应该传递const& 引用参数;
字符串、常量、需要转换的参数都可以传递给const&参数,但不能传递给非const的引用参数。
原理如下:
double dval = 3.14
const int& ival = dval;//this is legal for const references only
编译器会产生如下代码:
int temp = dval;
const int& ival = temp;
因此,如果是传递给非const引用,如果修改ival,则修改的是temp临时变量,而不是dval,这会让程序员陷入困境之中,因此,非const引用只能能绑定到与该引用同类型的对象上,而const引用,可以绑定到不同但相关的类型的对象或者绑定到右值(立即数)
例如:
float fsqrt(const float&);
void g(double d)
{
float r = fsqrt(2.0f); //传递的是保存2.0f的临时变量的引用
r = fsqrt(r); //传递r的引用
r = fsqrt(d); //传递的是保存float(d)的临时变量的引用
}
而对non_const引用不允许做类型转换,这样能够帮助我们避免一种引入临时变量而产生的可笑错误。例如:
void updata(float& i);
void g(double d, float r)
{
update(2.0f); //错误:non const参数,产生临时对象可能被修改
update(r); //传递r的引用
update(d); //错误:要去类型转换,产生临时对象可能被修改
}
4、异常
异常机制是c++中用于
将错误报告和错误处理分离开的手段。
一、一方报告出那些无法在局部解决的错误;
二、另一方处理那些在其他地方检查出的错误。
即一方抛出异常,一方捕获并处理异常。抛出异常采用throw,捕获采用catch,例如:
struct Range_error{
int i;
Range_error(int ii){i = ii;}
};
char to_char(int i)
{
if(i<min || i>max)
{
throw Range_error(i);
}
return i;
}
函数to_char()返回一个具有数值i的char,或者抛出一个Range_error异常。如果函数发现了一个无法处理的问题,就抛出异常,而希望他的调用者能够处理这个问题。如果想调用to_char()并捕获它可能抛出的异常,我们可以写:
void g(int i)
{
try{
char c = to_char(i);
//...
}
catch(Range_error){//异常处理器
cerr<<"oops\n";
}
}
5、析构函数与构造函数注意事项
1)析构函数可以是虚函数,并且在必定做父类的时候,析构函数必须声明为虚函数才可以得到期望的行为;
2)析构函数还可以是纯虚函数,一般声明纯虚函数是为了得到抽象类,纯虚函数可以有实现;
3)构造函数不能是虚函数,也不能是纯虚函数,在构造函数中,也不能调用虚函数,因为在父类的构造过程中,多态是被disable的,不能得到期望的行为;
4)同样,析构函数也不能调用虚函数;