《Effective C++》笔记:开篇
老早就读过这本书了,在实验室当工具书查阅用,一直都没空来总结.趁这几天空闲就把知识巩固一下,也当复习了
导读(P3)内容:C++里声明、定义、初始化是什么?
这东西在面试题几乎是喜闻乐见的。
声明
即表明变量的名称和类型,声明就是告诉编译器某个东西的名称和类型。
学过编译原理知道符号表这个东西,在词法分析的时候会把解析声明的变量及函数。
特别是函数的重载,声明时会给这个函数赋上另一个名称,如void fun(int)和void fun(int,int),编译器可能会弄这样两个名字fun_i1,fun_i2.用以识别具体调用哪一个函数。
定义
书中表明,定义是补充声明遗漏的细节,也是编译器为对象分配内存的地点。
我的理解是声明只告诉了编译器名称和类型,具体内容未知,而定义则把这部分内容完善,定义提供的代码列出函数的功能,类的成员,
编译器为函数、类分配内存地址。
初始化
即给对象赋初值,对用户自定义类型的对象而言,初始化由构造函数执行。C++的默认构造函数要不没有任何参数,要不就是每个参数都有缺省值。
概念性的东西差不多就这样,那么让我们来实战一下。
1 //声明 2 int i ; 3 bool b ; 4 class Widget; 5 void fun(); 6 7 int main() 8 { 9 10 return 0; 11 } 12 13 //类定义 14 class Widget 15 { 16 public: 17 Widget() 18 { 19 } 20 21 ~Widget() 22 { 23 } 24 25 private: 26 27 }; 28 29 //函数定义 30 void fun() 31 { 32 printf("doFun"); 33 }
代码中的声明的是全局变量,全局变量与局部变量的声明与定义,在内存方面有差异,这部分在内存管理章节再提。
初始化中的explicit关键字
把对象的构造函数声明为explicit,可以阻止该对象进行隐式类型转换。什么是隐式类型转换?看如下代码
class A { public: A(int x)//单参数构造函数 { } //explicit A(int x)//声明为explicit //{ //} ~A() { } void Test(int x); private: }; //函数定义 void fun(A a) { printf("%x\n",a); } int main() { fun(28);//调用fun,传入实参为int类型,编译运行成功 // fun(A(28)); return 0; }
如代码所示,fun需要的是一个类型为A的参数,传入int类型的参数编译运行成功,但实际上这样转换的隐患很大,在上述代码中编译器自作主张用28构造了一个新对象传给函数,打印对象的地址可以知道,虽然传入数值相同,
但跟原来的对象并不是一个东西,如果需要对该对象进行一些操作进行了隐式类型转换,结果必然与预料中不一致。
把代码中反注释一下并注释 - 行,即把单参数的构造函数声明为explicit,这时候调用fun(28)编译器会报错显示没有int到A的转换,此时调用fun(a)才能正确编译运行。
对象的Copy
C++中,如果在类中没有显式地声明一个copy构造函数,那么,编译器将会自动生成一个默认的copy构造函数,该构造函数完成对象之间的位拷贝。
下面举例说明copy构造函数的工作过程
class Widget { public: Widget() { } Widget(const Widget& rhs) { printf("调用copy构造函数\n"); } Widget &operator= (const Widget& chs) { Widget newWidget; //这里对chs进行复制操作 printf("调用copy assignment运算符\n"); return newWidget; } ~Widget() { } private: }; void fun(Widget w) { printf("调用fun\n"); } int main() { Widget w1; Widget w2(w1);//调用copy构造函数 Widget w3 = w2;//调用copy构造函数 w1 = w2;//调用copy assignment运算符 fun(w1);//调用copy构造函数 return 0; }
输出如下
其中Widget w3 = w2,与w1 = w2不同,分别是copy构造和copy赋值,这是因为一个新对象被定义,一定会有一个构造函数被调用,不可能调用赋值操作。而前者有新对象被定义,后者没有,所以造成copy调用的区别。
把w1作为参数传入fun的时候,这里是以值传递的方式传给fun的,因此在调用的同时,fun会自己动调用copy构造函数把w1复制到w这个临时副本里,造成效率下降,这往往与设计者的初衷相违背。因此,书中的条款20:Pass-by-reference-to-const详细介绍了值传递与址传递的优劣,这个以后再说。
关于拷贝,自然免不了有浅拷贝和深拷贝之分。编译器生成的默认复制构造函数是浅拷贝,因此自定义复制构造函数覆盖默认复制构造函数是良好的编程风格,可以避免错误的产生,提高代码效率。这在条款5:Know what functions C++ silently writes and calls和条款6:Explicitly disallow the use of compiler-generated functions you do now want中有详细介绍。
OK,导读部分大概就是这么多知识。博客写得少,措词有点捉急啊,不过自己的理解总比直接把书中的原话打上来好。写这点字就这么费劲,感觉对不起以前教育我的语文老师~