zoukankan      html  css  js  c++  java
  • 《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
    ",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构造函数
    "); 
        } 
        Widget &operator= (const Widget& chs) 
        { 
            Widget newWidget; 
            //这里对chs进行复制操作 
            printf("调用copy assignment运算符
    "); 
            return  newWidget; 
        } 
        ~Widget() 
        { 
        }
    
    private: 
    };
    
    void fun(Widget w) 
    { 
        printf("调用fun
    "); 
    }
    
    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,导读部分大概就是这么多知识。博客写得少,措词有点捉急啊,不过自己的理解总比直接把书中的原话打上来好。写这点字就这么费劲,感觉对不起以前教育我的语文老师~

  • 相关阅读:
    cookie操作和代理
    发起post请求
    scrapy核心组件
    爬取多个url页面数据--手动实现
    scrapy之持久化存储
    selenium + phantomJs
    scrapy框架简介和基础使用
    校验验证码 实现登录验证
    beautifulsoup解析
    xpath
  • 原文地址:https://www.cnblogs.com/destino74/p/3954383.html
Copyright © 2011-2022 走看看