黑马程序员技术交流社区

标题: 【笔记】C++程序员必知十大条款(上) [打印本页]

作者: 倾心莫若初见    时间: 2016-10-11 11:38
标题: 【笔记】C++程序员必知十大条款(上)
本帖最后由 倾心莫若初见 于 2016-12-27 10:47 编辑

       切记:一定要用心去消化吸收这些知识,不可走马观花。
条款01:视C++为一个语言联邦
    为了更好的理解C++,我们将C++分解为四个主要次语言:
   请记住:

    条款02:尽量以constenuminline替换#define

这个条款或许可以改为“宁可 以编译器替换预处理器”。即尽量少用预处理。
    编译过程:.c文件--预处理-->.i文件--编译-->.o文件--链接-->bin文件
    预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。检查包含预处理指令的语句和宏定义,并对源代码进行相应的转换。预处理过程还会删除程序中的注释和多余的空白字符。可见预处理过程先于编译器对源代码进行处理。预处理指令是以#号开头的代码行。
    例:#define ASPECT_RATIO 1.653
    记号名称ASPECT_RATIO也许从未被编译器看见,也许在编译器开始处理源代码之前它就被预处理器移走了。即编译源代码时ASPECT_RATIO已被1.653取代。ASPECT_RATIO可能并未进入记号表(symbol table)。
    替换:const doubleAspectRatio = 1.653;
    好处应该有:多了类型检查,因为#define 只是单纯的替换,而这种替换在目标码中可能出现多份1.653;改用常量绝不会出现相同情况。
    常量替换#define两点注意:
     const char *authorName = “Shenzi”;
       cosnt std::string authorName("Shenzi");
     static const int NumTurns = 5;//static 静态常量 所有的对象只有一份拷贝。
    万一你编译器不允许“static整数型class常量”完成“in calss初值设定”(即在类的声明中设定静态整形的初值),我们可以通过枚举类型予以补偿:
      enum { NumTurns = 5 };
    *取一个const的地址是合法的,但取一个enum的地址就不合法,而取一个#define的地址通常也不合法。如果你不想让别人获取一个pointer或reference指向你的某个整数常量,enum可以帮助你实现这个约束。
     例:#define CALL_WITH_MAX(a,b)   f((a) > (b)) ? (a) : (b))
     宏看起来像函数,但不会招致函数调用带来的额外开销,而是一种简单的替换。
     替换:
      template<typename T>
      inline void callWithMax(cosnt T&a, cosnt T &b)
      {
            f(a > b ? a : b);
       }
      callWithMax是个真正的函数,它遵循作用于和访问规则。
      请记住:

    条款03:尽可能使用const
    const允许你告诉编译器和其他程序员某值应保持不变,只要“某值”确实是不该被改变的,那就该确实说出来。
    关键字const多才多艺:
    例:
       chargreeting[] = "Hello";
        char *p = greeting;    //指针p及所指的字符串都可改变;
        const char *p = greeting;    //指针p本身可以改变,如p = &Anyother;p所指的字符串不可改变;
        char * cosnt p = greeting;    //指针p不可改变,所指对象可改变;
        const char * const p = greeting;    //指针p及所致对象都不可改变;
     说明:
    STL例子:
        const std::vector<int>::interatoriter = vec.begin();//作用像T *const, ++iter 错误:iter是const
        std::vector<int>::const_iterator cIter = vec.begin();//作用像const T*,*cIter = 10 错误:*cIter是const
    以下几点注意:
    例:const Rationaloperator* (const Rational &lhs, cosnt Rational &rhs);
    说明:声明为const的成员函数,不可改变non-static成员变量,在成员变量声明之前添加mutable可让其在const成员函数中可被改变。
    const_cast<char &>(static_cast<const TextBlock&>(*this))[position];
    //static_cast 将TextBlock &转为const TextBlock &;
    //const_cast将返回值去掉const约束;
    请记住:


   条款04:确定对象被使用前已先被初始化
    永远在使用对象之前先将它初始化。对于无任何成员的内置类型,你必须手工完成此事。至于内置类型以外的任何其它东西,初始化责任落在构造函数身上,确保每一个构造函数都将对象的每一个成员初始化。
    赋值和初始化:
    C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。所以应将成员变量的初始化置于构造函数的初始化列表中。
     ABEntry::ABEntry(const std::string& name, conststd::string& address,
                                   const std::list<PhoneNumber>& phones)
     {
           theName = name;               //这些都是赋值,而非初始化
            theAddress =address;          //这些成员变量在进入函数体之前已调用默认构造函数,接着又调用赋值函数,
            thePhones =phones;            //即要经过两次的函数调用。           
            numTimesConsulted = 0;
    }

    ABEntry::ABEntry(const std::string& name, const std::string&address,
                                  const std::list<PhoneNumber>& phones)
        : theName(name),                   //这些才是初始化
       theAddress(address),               //这些成员变量只用相应的值进行拷贝构造函数,所以通常效率更高。
        thePhones(phones),
        numTimesConsulted(0)
        {    }
    所以,对于非内置类型变量的初始化应在初始化列表中完成,以提高效率。而对于内置类型对象,如numTimesConsulted(int),其初始化和赋值的成本相同,但为了一致性最好也通过成员初始化表来初始化。如果成员变量时const或reference,它们就一定需要初值,不能被赋值。
      C++有着十分固定的“成员初始化次序”。基类总是在派生类之前被初始化,而类的成员变量总是以其说明次序被初始化。所以:当在成员初始化列表中列各成员时,最好总是以其声明次序为次序。
    请记住:

.构造/析构/赋值运算
    几乎你写的每个类都会有一或多个构造函数、一个析构函数、一个拷贝赋值操作符。如果这些函数犯错,会导致深远且令人不愉快的后果,遍及整个类。所以确保它们行为正确时生死攸关的大事。

    条款05:了解C++默默编写并调用哪些函数
    如果你自己美声明,编译器就会为类声明(编译器版本的)一个拷贝构造函数,一个拷贝赋值操作符和一个析构函数。此外如果你没有声明任何构造函数,编译器也会成为你声明一个默认构造函数。所有这些函数都是public且inline。
     惟有当这些函数被需要(被调用),它们才会被编译器创建出来。即有需求,编译器才会创建它们。
     默认构造函数和析构函数主要是给编译器一个地方用来放置“藏身幕后”的代码,像是调用基类和非静态成员变量的构造函数和析构函数(要不然它们该在哪里被调用呢??)。
     注意:编译器产生的析构函数是个non-virtual,除非这个类的基类自身声明有virtual析构函数。
     至于拷贝构造函数和拷贝赋值操作符,编译器创建的版本只是单纯地将来源对象的每一个非静态成员变量拷贝到目标对象。
     如一个类声明了一个构造函数(无论有没参数),编译器就不再为它创建默认构造函数。
     编译器生成的拷贝赋值操作符:对于成员变量中有指针,引用,常量类型,我们都应考虑建立自己“合适”的拷贝赋值操作符。因为指向同块内存的指针是个潜在危险,引用不可改变,常量不可改变。
    请记住:


本篇已结束,请看【笔记】C++程序员必知十大条款(下)




精华推荐:

3分钟带你读懂C/C++学习路线
为什么来黑马程序员学C/C++? 稳做IT贵族人才!
2016最新C++学习路线图(附完整视频资源)+ 笔记 + 工具 + 面试题总结!


作者: sunrongli    时间: 2016-10-11 12:57
资源不错





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2