C++那些事笔记——const那些事

·本篇:1.4k字 大约需要: 5分钟

前言

C++那些事

这是一个适合新手学习的C++开源项目

[Github地址] (https://github.com/Light-City/CPlusPlusThings)


const那些事

1.const的作用

  • 可以定义常量

    创建常量的通用格式如下:

    1
    const type name = value;

    当你在声明常量的时候必须对它进行初始化,如果在声明常量时没有对它进行初始化,则该常量的值是不确定的,且无法修改。

  • 类型检查

    • const常量与#define宏定义常量的区别:

      • const能够明确指定类型,并且可以使用C++的作用域规则将定义限制在特定的函数或文件中
      • const定义的变量只有类型为整型或枚举时,且以常量表达式初始化时才能作为常量表达式
      • 其他情况下它只是一个const限定的变量而不是一个常量
    • 防止修改,起保护作用,增加程序健壮性

    • 可以节省空间,避免不必要的浪费

      • const定义常量从汇编角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数
      • const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中却有多份拷贝

2.const对象默认为局部变量

  • 未被const修饰的变量不需要extern显式声明
  • const常量需要显式声明extern,并且需要做初始化
  • 常量在定义后就不能被修改,所以定义时必须初始化

3.指针与const

  • 如果const在*的左侧,则const就是用来修饰指针所指向的变量,即指针指向常量

    1
    2
    const char * a; //指向常量的指针。
    char const * a; //同上
    • 对于指向常量的指针,不能通过指针来修改对象的值
    • 允许把非const对象的地址赋给指向常量的指针,当要修改其值时需要像通过非const指针修改
    1
    2
    3
    4
    int val = 3;
    const int *ptr = &val; //ok
    int *ptr1 = &val;
    *ptr = 4;
    • 不能使用void *指针去保存const对象的地址,必须使用const void *类型的指针保存const对象的地址
    1
    2
    3
    const int p = 10;
    const void * vp = &p;
    void *vp = &p; //error
  • 如果const在*的右侧,则const就是修饰指针本身,即指针本身就是常量,称为常指针

    • 常指针在定义是必须进行初始化,且常指针的值不能再进行修改,但是可以通过非const指针来修改

      1
      2
      3
      4
      int num=0;
      int * const ptr=# //const指针必须初始化,且const指针的值不能修改
      int * t = #
      *t = 1;
    • 因为此时const修饰的是指针本身,ptr指向的还是一个变量,只是不能通过ptr修改它的值,所以不能将const对象的地址赋值给ptr

    • 上述情况若改为const int *ptr或const int *const ptr就可以正常运行

  • 上述两种情况的结合就是指向常量的常指针

    • ptr是一个const指针,然后它指向了一个int类型的const对象

      1
      2
      const int p = 3;
      const int * const ptr = &p;

4.函数与const

  • const修饰函数返回值

    这个和cosnt修饰普通变量和指针含义基本相同,但没有意义

  • const修饰函数参数

    • 输入参数只是”值复制”,对原变量没有影响,无需保护,即使传入的形参是指针也是一样,const修饰的是指针本身,它所指向的地址还是可变的,所以此操作无意义

      1
      2
      void func(const int var);
      void func(int *const var);
    • 参数指针所指内容为常量不可变

      1
      void StringCopy(char *dst, const char *src);

      dst是输出参数,src是输入参数,当我们需要保护src指向对象的值时,将src用const修饰,这样当函数体的语句试图修改src的值时,编译器将指出错误

    • 参数为引用,增加效率同时防止修改

      1
      void func(const A &a)
      • 对于非内部数据类型的参数而言,象void func(A a) 这样声明的函数注定效率比较低。因为函数体内将产生A 类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间

      • 为了提高效率,可以将函数声明改为void func(A &a),因为“引用传递”仅借用一下参数的别名而已,不需要产生临时对象

      • 但是函数void func(A &a) 存在一个缺点:“引用传递”有可能改变参数a,这是我们不期望的

      • 解决这个问题很容易,加const修饰即可,因此函数最终成为 void func(const A &a)。

      • 以此类推,是否应将void func(int x) 改写为void func(const int &x),以便提高效率?

      • 没有必要,因为内部数据的参数不存在构造、析构之内的过程,“值传递”和“引用传递”的效率几乎相当

5.类与const

  • 在一个类中,任何不会修改数据成员的函数都应该声明为const类型,如果在编写const成员函数时,不慎修改数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性

  • 使用const关键字进行说明的成员函数,称为常成员函数,只有常成员函数才有资格操作常量或常对象,没有使用const关键字进行说明的成员函数不能用来操作常对象

  • 对于类中的const成员变量必须通过初始化列表进行初始化