C++中的const可用于修饰变量函数,且在不同的地方有着不同的含义,现总结如下。
  Const的语义
  C++中的const的目的是通过编译器来保证对象的常量性,强制编译器将所有可能违背const对象的常量性的操作都视为error。
  对象的常量性可以分为两种:物理常量性(即每个bit都不可改变)和逻辑常量性(即对象的表现保持不变)。C++中采用的是物理常量性,例如下面的例子:
  struct A {
  int *ptr;
  };
  int k = 5, r = 6;
  const A a = {&k};
  a.ptr = &r; // !error
  *a.ptr = 7; // no error
  a是const对象,则对a的任何成员进行赋值都会被视为error,但如果不改动ptr,而是改动ptr指向的对象,编译器不会报错。这实际上违背了逻辑常量性,因为A的表现已经改变了!
  逻辑常量性的另一个特点是,const对象中可以有某些用户不可见的域,改变它们不会违背逻辑常量性。Effective C++中的例子是:
  class CTextBlock {
  public:
  ...
  std::size_t length() const;
  private:
  char *pText;
  std::size_t textLength;            // last calculated length of textblock
  bool lengthIsValid;                // whether length is currently valid
  };
  CTextBlock对象每次调用length方法后,都会将当前的长度缓存到textLength成员中,而lengthIsValid对象则表示缓存的有效性。这个场景中textLength和lengthIsValid如果改变了,其实是不违背CTextBlock对象的逻辑常量性的,但因为改变了对象中的某些bit,会被编译器阻止。C++中为了解决此问题,增加了mutable关键字。
  本部分总结:C++中const的语义是保证物理常量性,但通过mutable关键字可以支持一部分的逻辑常量性。
  Const修饰变量
  如上节所述,用const修饰变量的语义是要求编译器去阻止所有对该变量的赋值行为。因此,必须在const变量初始化时提供给它初值:
  const int i;
  i = 5; // !error
  const int j = 10; // ok
  这个初值可以是编译时即确定的值,也可以是运行期才确定的值。如果给整数类型的const变量一个编译时初值,那么可以用这个变量作为声明数组时的长度:
  const int COMPILE_CONST = 10;
  const int RunTimeConst = cin.get();
  int a1[COMPLIE_CONST]; // ok in C++ and error in C
  int a2[RunTimeConst]; // !error in C++
  因为C++编译器可以将数组长度中出现的编译时常量直接替换为其字面值,相当于自动的宏替换。(gcc验证发现,只有数组长度那里直接做了替换,而其它用COMPILE_CONST赋值的地方并没有进行替换。)
  文件域的const变量默认是文件内可见的,如果需要在b.cpp中使用a.cpp中的const变量M,需要在M的初始化处增加extern:
  //a.cpp
  extern const int M = 20;
  //b.cpp
  extern const int M;
  一般认为将变量的定义放在.h文件中会导致所有include该.h文件的.cpp文件都有此变量的定义,在链接时会造成冲突。但将const变量的定义放在.h文件中是可以的,编译器会将这个变量放入每个.cpp文件的匿名namespace中,因而属于是不同变量,不会造成链接冲突。(注意:但如果头文件中的const量的初始值依赖于某个函数,而每次调用此函数的返回值不固定的话,会导致不同的编译单元中看到的该const量的值不相等。猜测:此时将该const量作为某个类的static成员可能会解决此问题。)