effective c++系列:const详解

const关键字

  • const名叫常量限定符,用来限定特定变量,以通知编译器该变量是不可修改的。习惯性的使用const,可以避免在函数中对某些不应修改的变量造成可能的改动。

const和#define的区别

1
2
const int a = 0; 
#define b 200
  • 编译器处理方式不同
    • #define在预处理阶段展开。
      const在编译期间使用。
  • 类型和安全检查方式不同
    • #define宏没有类型,不做任何类型检查,仅仅是展开。
      const常量有具体的类型,在编译阶段会执行类型检查。
  • 存储方式不同
    • define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。
      const常量会在内存中分配(可以是堆中也可以是栈中)。
  • 作用域
    • #define只能定义全局变量 const可以定义类内专属常量。
      1
      2
      3
      4
      5
      6
      7
      class A{
      public:
      const int a = 9;
      };
      A obj;
      cout << obj.a << endl;//ok
      cout << a << endl;//error!only visible in class
  • const的几个优点
    • 编译器可以检查const类型 #define只是进行替换。
    • 调试器可以对const进行调试,但不能对宏变量进行调试。
  • 对于常量,尽量使用const、enum代替#define
  • 对于函数宏定义,尽量使用inline函数代替#define

const的内存分配

  • 对于基础类型(整数,浮点数,字符) 系统不会给const变量开辟空间,会将其放到符号表中。
  • 对const变量取地址的时候 系统就会给它开辟空间。
  • 当用变量给const变量赋值时,系统直接为其开辟空间而不会把它放入符号表中。
  • const 自定义数据类型(结构体、对象) 和数组系统会分配空间。

主要功能和用法

  • 在class外部修饰全局变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const int a = 1;
    #include <iostream>
    using namespace std;
    extern const int a;
    int main()
    {
    int *p = (int *)(&a);
    *p = 8;//error!Exception has occurred.Segmentation fault
    cout << "a=" << a << endl;
    cout << "*p=" << *p;
    return 0;
    }
  • 修饰namespace作用域中的常量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <iostream>
    namespace hqin{
    const int a = 0;

    } // namespace hqin
    int main()
    {
    hqin::a = 1; //error: assignment of read-only variabl 'hqin::a'
    return 0;
    }
  • 修饰文件、函数或者作用域中被声明为static的对象
    • 不希望改变static变量时使用。
  • 通常情况下如果不希望改变文件指针、函数内部等更改数据,就命名为const类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template<typename T>
    class A{
    public:
    //不希望改变x,y,函数内部也不希望改变data
    inline T sum (const T& x, const T& y) const{
    return x + y;
    }
    };
    A<complex<int>> test = A<complex<int>>();
    complex<int> a(1,1);
    complex<int> b(1,1);
    cout << test.sum(a,b);
  • 修饰class内部的static和non-static成员变量
    1
    2
    3
    4
    5
    class A{
    public:
    const int x = 0;
    const static int y = 0;
    };
  • 修饰指针、指针所指物
    1
    2
    3
    4
    5
    int * const p1; // p1:只读  *p1:变量
    const int *p2; // p2:变量 *p2:只读
    int const *p3; // p3:变量 *p3:只读
    int const * const p4; // p4:只读 *p4:只读
    const int * const p5; // p5:只读 *p5:只读

const初始化

  • 在c++ 11 之前,普通变量、常量(const)、静态(static)、静态常量(static const)的初始化和声明如下表所示。

    type 声明时初始化 初始化列表初始化 构造函数内初始化 类外初始化
    普通变量 x x (整型除外)
    const x x x
    static x x x
    static const x x x
  • c++ 11之后,如下表所示。

    type 声明时初始化 初始化列表初始化 构造函数内初始化 类外初始化
    普通变量 x (整型除外)
    const x x x
    static x x x
    static const x x x