C++程序设计中,const限定符和很多方面都有关系,比如说引用、数组、指针、函数等等,这里对const限定符的使用做下小结。
1. 定义const对象
const限定符将一个对象转换成一个常量,定义后就不能修改,因此必须在定义时进行初始化。
const int bufSize = 512;
2. const对象默认为文件的局部变量
在全局作用域声明的const变量是定义该对象文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。通过指定const变量为extern,就可以在整个程序中访问const对象:
// file_1.cc
// defines and initializes a const that is accessible to other files
extern const int bufSize = fcn();
// file_2.cc
extern const int bufSize; // uses bufSize from file_1
for (int index = 0; index != bufSize; ++index)
//...
3. const引用
const引用是指向const对象的引用:
const int ival = 1024;
const int &refVal = ival; // ok: both reference and object are const
int &ref2 = ival; // error: nonconst reference to a const object
const引用可以初始化不同类型的对象或者初始化为右值,如字符值常量:
int i = 42;
// legal for const reference only
const int &r = 42;
const int &r2 = r + i;
同样的初始化对非const引用却是不合法的。也就是说,非const引用只能绑定到与该引用同类型的对象,const引用则可以绑定到不同但相关的类型的对象或绑定到右值。
4. 指针和const限定符
4.1 指向const对象的指针
如果指针指向const对象,则不允许用指针来改变其所指的const值。为了保证这个特性,C++语言强制要求指向const对象的指针也必须具有const特性:
const double *cptr; // cptr may point to a double that is const
这里的cptr是一个指向double类型const对象的指针,const限定了cptr指针所指向的对象类型,而并非cptr本身。也就是说,cptr本身并不是const,在定义时不需要对其进行初始化。如果需要的话,允许cptr重新赋值,使其指向另一个const对象。把一个const对象的地址赋给一个普通的、非const对象的指针也会导致编译错误,但允许把非const对象的地址赋给指向const对象的指针,该指针指向的值不能被修改。
const double pi = 3.14;
double *ptr = π // error: ptr is a plain pointer
const double *cptr = & pi; // ok; cptr is a pointer to const
4.2 const指针
const指针表示指针是const,指针的值不能修改,这就意味着不能使其指向其他对象,也必须在定义时初始化。
int errNumb = 0;
int *const curErr = &errNumb;
指针本身是const并没有说明是否能修改指针所指向的对象的值,指针所指向的对象的值能够被修改完全取决于该对象的类型。如果curErr指向一个普通的非常量int型对象ErrNumb,则可使用curErr修改该对象的值。
4.3 指向const对象的const指针
const double pi = 3.14159;
// pi_ptr is const and points to a const object
const double *const pi_ptr = π
本例中,既不能修改pi_ptr所指向对象的值,也不允许修改该指针的指向。
5. const对象与数组初始化
数组在定义时,其维数必须用值大于等于1的常量表达式定义。此常量表达式只能包含整型字面值常量、枚举常量或者用常量表达式初始化的整型const对象。非const变量以及要到运行阶段才能知道其值的const变量都不能用于定义数组的维数。
const unsigned buf_size = 512;
char input_buffer[buf_size]; // ok: const variable
const unsigned sz = get_size();
int valz[sz]; // error: size not known until run time
6. const形参
6.1 非引用形参
在调用函数时,如果该函数使用非引用的非const形参,则既可给该函数传递const实参,也可传递非const实参。
如果将形参定义为非引用的const类型:
void fcn(const int i) {/* fcn can read but not write to i */}
则在函数中,不可以改变形参的值。由于实参仍然是以副本的形式传递,也就是说实参的值不受函数调用的影响,因此传递给fcn的既可以是const对象也可以是非const对象。
6.2 引用形参
在向函数传递大型对象时,需要使用引用形参,因为复制实参对于大部分的类类型或者大型数组,效率太低。编写一个比较两个string对象长度的函数作为例子。这个函数需要访问每个string对象的size,但不必修改这些对象。由于string对象可能相当长,所以我们希望避免复制操作。使用const引用就可避免复制:
//compare the length of two strings
bool isShorter(const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
如果函数具有普通的非const引用形参,则显然不能通过const对象进行调用。非const引用形参只能与完全同类型的非const对象关联。