《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 }
View Code

代码中的声明的是全局变量,全局变量与局部变量的声明与定义,在内存方面有差异,这部分在内存管理章节再提。

初始化中的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; 
}
View Code

如代码所示,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; 
}
View Code

输出如下

QQ截图20140904055207

其中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,导读部分大概就是这么多知识。博客写得少,措词有点捉急啊,不过自己的理解总比直接把书中的原话打上来好。写这点字就这么费劲,感觉对不起以前教育我的语文老师~

posted @ 2014-09-03 19:01  Destino74  阅读(283)  评论(0编辑  收藏  举报