1. 概念
C++提供了用户定义常量的概念,const就是为了直接表达“不变的值”这样一个概念。这种东西在一些环境中非常有用,例如,许多对象在初始化之后就不再改变自己的值了;与直接将字面值常量散布在代码中相比,采用符号常量写出的代码更容易维护;指针常常是边读边写,而不是边写边移动;许多函数参数是只读不写的。——摘自《The C++ Programming Language》
2. 使用
① 常量
const int i = 0;
const int a[] = {1, 2, 3, 4};
i = 1; //error C3892: 'i' : you cannot assign to a variable that is const
a[1] = 5; //error C3892: 'a' : you cannot assign to a variable that is const
因为不允许赋值,所以常量必须进行初始化。
const int i; //error C2734: 'i' : const object must be initialized if not extern
注意:在C++中尽量以const替代#define(Prefer const to #define), 以下是《Effective C++》中的解释:
② 指针和引用
在C++里有常量指针和指针常量:const char*, char const*, char*const。 一种是用来修饰指针所指向的变量(即指针所指向的变量不可变),另一种是修饰指针(即指针本身为常量),详细参见指针常量和常量指针
如果想要指针所指向的对象和指针本身都保持不可变:const char *const p;
对于引用,以 指针常量和常量指针 里相同的方式去理解和使用:把一个声明从右向左读( & 读成 reference to )
const int &a = i; //a is a reference to const int
因为引用只能在定义时被初始化一次,之后不能改变,所以const修饰引用本身(int &const)是不需要的
③ 函数
const 修饰函数参数:
当const修饰函数参数时,如果const修饰的是传递对象本身,不能保证传递对象的不变,因为修饰的本身是形参,如:
void Function(const int p);
void Function(int *const p);
虽然如此,但将传递对象本身修饰为const也还是有好处的,可以避免后面提到的一些特殊情况。更多地是用const修饰传递对象(如指针或引用)所指向或引用的对象,因为可以保证所指向或引用的对象不变,如:
void Function(const int *p);
void Function(const int &p);
const 修饰函数返回值:一般很少用到,和修饰函数参数一样,const修饰返回对象本身不能保证返回对象的不变,因为修饰的其实是其副本,如:
const int Function();
int *const Function();
虽然如此,但将返回对象本身修饰为const也还是有好处的,可以避免后面提到的一些特殊情况,当const修饰返回对象(如指针或引用)所指向或引用的对象时,保证了所指向或引用的对象的不变, 如:
const int *Function();
const int &Function();
特殊情况:当我们不小心写出了如下的代码时,《Effective c++》中称之为暴行
if (parameter = a) { ... }
if (Add(a,b) = c)
很显然我们的原意是做判断而不是赋值,但是这种错误编译器又捕捉不到,可是当我们将参数和返回值声明为const时,编译器就可以捕捉到,因为不能再赋值给常量。
④ 类
const修饰类成员变量:
被const修饰的成员变量在初始化后就不能被修改,而且只能在构造函数的初始化列表中初始化
class A
{
public:
A(int x): value(x) { } ; //只能在构造函数初始化列表中初始化
private:
const int value; //成员常量不能被修改
};
const修饰成员函数:const修饰的成员函数称之为常成员函数,一般const放在最后来修饰,常成员函数不能改变类的数据成员;常成员函数不能调用类中的非const成员函数,因为调用非const成员函数可能间接改变类的数据成员。
class A
{
public:
...
int GetValue() const { return value; }
...
};
const修饰类对象/对象指针/对象引用:
const 修饰对象称之为常对象,这里的修饰对象指针和对象引用指的是 指针所指向的对象和引用所引用的对象为常对象, 常对象的所有类数据成员都不能被改变,并且不能调用任何非const成员函数,因为那会间接改变类成员。
class A
{
void Function1() const;
void Function2();
}
const A object;
object.Function1(); //OK
object.Function2(); //Error
const A *pObject = new A();
pObject ->Function1(); //OK
pObject ->Function2(); //Error
⑤ const_cast
#include <iostream>
using namespace std;
class A
{
public:
A(int x):value(x){}
public:
int value;
};
int main()
{
const A a(0);
cout<<a.value<<endl;
A *pa = const_cast<A *>(&a);
pa->value = 1;
cout<<a.value<<endl;
A &ra = const_cast<A &>(a);
ra.value = 2;
cout<<a.value<<endl;
return 0;
}
#include <iostream>
using namespace std;
int main()
{
const int a = 0;
cout<<a<<endl;
int *pa = const_cast<int *>(&a);
*pa = 1;
cout<<a<<", "<<*pa<<endl;
int &ra = const_cast<int &>(a);
ra = 2;
cout<<a<<", "<<ra<<endl;
return 0;
}