读书笔记

  • 尽量以const,enum,inline替换#define
    •   用宏定义一个常量的时候,可能会发生一些错误。当发生错误的时候你可能并不能知道这个错误具体出自哪里,因为宏定义的变量可能没有进入记号表中(symbol table)。还有就是宏定义的东西可能会盲目的置换代码中的变量(直接全部置换过去,括号可能会影响最终的结果),会导致出错。
    •   当我们要定义一个常量去替换#define时,有两种情况
      •   ①定义一个指向常量的常量指针,可以这么定义:const std::string author(''fuzhiqiang'')
      •   ②定义一个class专属常量,#define不具有封装性,不能够定义class类的专属常量
    •   枚举enum在很多地方和#define很像。他们都是不允许去得到他们的一个地址。而const取地址是合法的。
    •   但是#ifdef和#ifndef控制编译功能十分重要
    • 对于单纯的常量,最好用const或者enum;对于形似函数的宏最好用inline函数


  • 尽可能使用const
    •   指针使用const
      •   常量指针:char* const pContent  ,是一个指针,它指向的内容是不能变的,但是*pcontent可以改变,通过改变*pcontent可以改变它所指的值
      •   指针常量:const char *pContent,指针所指向的内容是常量,不能改变,就是*pcontent不能改变,pcontent可以改变
      •   指向常量的常量指针:const char* const pContent,都不能改变
    •   函数使用const
      •   参数为引用,为了增加效率同时防止修改。修饰引用参数时:

          void function(const Class& Var); //引用参数在函数内不可以改变

          void function(const TYPE& Var); //引用参数在函数内为常量不可变

        这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本, 然后传递过去,而它直接传递地址,所以这种传递比按值传递更有效.另外只有引用的const传递可以传递一个临时对象,因为临时对象都是const属性, 且是不可见的,他短时间存在一个局部域中,所以不能使用指针,只有引用的const传递能够捕捉到这个家伙.

    •   类使用const
      •   const修饰成员变量:修饰的成员变量不能被修改
      •   const修饰成员函数:const修饰类的成员函数,则该成员函数不能修改类中任何非const成员函数。一般写在函数的最后来修饰。常成员函数, 它不改变对象的成员变量,也不能调用类中任何非const成员函数。
      • 对于const类对象/指针/引用,只能调用类的const成员函数,因此,const修饰成员函数的最重要作用就是限制对于const对象的使用。
        a. const成员函数不被允许修改它所在对象的任何一个数据成员。
        b. const成员函数能够访问对象的const成员,而其他成员函数不可以。
    •   将const类型转换为非const类型
      • 用法:const_cast <type_id>  (expression) 
        该运算符用来修改类型的constvolatile属性。除了const volatile修饰之外, type_idexpression的类型是一样的。


    •   建议  
      •   要大胆的使用const,这将给你带来无尽的益处,但前提是你必须搞清楚原委;
      •   要避免最一般的赋值操作错误,如将const变量赋值,具体可见思考题;
      •   在参数中使用const应该使用引用或指针,而不是一般的对象实例,原因同上;
      •   const在成员函数中的三种用法(参数、返回值、函数)要很好的使用;
      •   不要轻易的将函数的返回值类型定为const;
      •   除了重载操作符外一般不要将返回值类型定为对某个对象的const引用;
      •   任何不会修改数据成员的函数都应该声明为const 类型。

  • 确定对象在被使用前已经被初始化
    •   array的内容没有初始化,而vector的内容确是初始化了的
    •   内置类型:bool,char,w_char,short,int,long,float,double,long double;这些初始化都手动完成
    •   类的初始化:一般采用成员列表初始化,而不是单一的赋值
    • class myclass{
      private:
          string Name;
          int Id;
          string Sex;
      public:
          myclass(cosnt string & name,cosnt int & id,const string & sex):
              Name(name),Id(id),Sex(sex){}
      };
      View Code
      •   这其中初始化的次序有讲究的:按照变量声明的次序来进行成员列表初始化
    •   不同的编译单元内定义的  non-local static变量
      •   这里先解释一下名词,static是静态的意思,表示这个变量不由栈分配,而存储在特有的全局变量/静态变量区域中,具有长寿命的特点(从被构造出来,直到程序结束时,才会由系统释放资源);而non-local则是说这个对象是全局的,而不是函数内的静态变量,是说它的作用范围广。
      • 比如在文件1中定义了

        int a = 1;

        而在文件2中又会去使用:

        extern int a;
        int b = a *3;

        可以看到文件1应在文件2之后执行,这样a才能获得初值,否则b得到的将是垃圾值,但事实上C++对于不同文件执行的相对次序并无明确定义,这样b究竟得到的是垃圾值还是3就不能确定。

        解决这个问题是方法是不要使变量有全局的作用域,可以在文件1中定义:

        1 int& GetA()
        2 {
        3          static int a = 1;
        4          return a;
        5 }

        而在文件2中调用

        int b = GetA();

        这样就一定保证a的初始化在先了。

原文地址:https://www.cnblogs.com/Kobe10/p/5735541.html