让人头晕的const
看到const关键字,C++程序员首先想到的可能是const常量. 但是实际上并没有这么简单。
1一般常量和对象常量
1.1 一般常量
一般常量是指简单类型的常量。定义时,修饰符const可以放在常量类型前,也可以放在常量类型后。如:
int const x=2;
和
const int x=2;
是等价的。
定义或说明一个常数组可采用如下语法:
<类型说明符> const <数组名>[<大小>]…
或者
const <类型说明符> <数组名>[<大小>]…
例如:
int const a[5]={1, 2, 3, 4, 5};
1.2常对象
常对象是指对象常量,定义格式如下:
<类名> const <对象名>
或者
const <类名> <对象名>
定义常对象时,同样要进行初始化,并且该对象不能再被更新,修饰符const可以放在类名后面,也可以放在类名前面。
2.常指针和常引用
一般常量和对象常量是很直观的,很容易理解,但是指针相关的const就稍微有点复杂了。
使用const修饰指针时,由于const的位置不同,而含意不同。下面举两个例子,说明它们的区别。
记住一个原则,根据const 相对于 * 的位置,“数据向左,指针向右”
意思是如果const位于*的左边,那么指针指向的数据是常量,否则,指针本身为常量.
下面定义的一个指向字符串的常量指针:
char * const ptr1 = string1;
其中,ptr1是一个常量指针。因此,下面赋值是非法的。
ptr1 = string2;
而下面的赋值是合法的:
*ptr1 = "m";
因为指针ptr1所指向的变量是可以更新的,不可更新的是常量指针ptr1所指的方向(别的字符串)。
下面定义了一个指向字符串常量的指针:
const char * ptr2 = string1;
其中,ptr2是一个指向字符串常量的指针。ptr2所指向的字符串不能更新的,而ptr2是可以更新的。因此,
*ptr2 = "x";
是非法的,而:
ptr2 = string2;
是合法的。
所以,在使用const修饰指针时,应该注意const的位置。定义一个指向字符串的指针常量和定义一个指向字符串常量的指针时,const修饰符的位置不同,前者const放在*和指针名之间,后者const放在类型说明符前。
3.const 函数
const的一些强大的功能基于它在函数声明中的应用。
在一个函数声明中,const可以指的是函数的返回值,或某个参数;对于成员函数,还可以指的是整个函数。
3.1 常成员函数
const成员函数的目的当然是为了指明哪个成员函数可以在const对象上被调用。但很多人忽视了这样一个事实:
仅在const方面有不同的成员函数可以重载。这是c++的一个重要特性。再次看这个string类:
class string {
public:
...
// 用于非const对象的operator[]
char& operator[](int position)
{ return data[position]; }
// 用于const对象的operator[]
const char& operator[](int position) const
{ return data[position]; }
private:
char *data;
};
string s1 = "hello";
cout << s1[0]; // 调用非const
// string::operator[]
const string s2 = "world";
cout << s2[0]; // 调用const
// string::operator[]
通过重载operator[]并给不同版本不同的返回值,就可以对const和非const string进行不同的处理:
string s = "hello"; // 非const string对象
cout << s[0]; // 正确——读一个
// 非const string
s[0] = 'x'; // 正确——写一个
// 非const string
const string cs = "world"; // const string 对象
cout << cs[0]; // 正确——读一个
// const string
cs[0] = 'x'; // 错误!——写一个
// const string
另外注意,这里的错误只和调用operator[]的返回值有关;operator[]调用本身没问题。
错误产生的原因在于企图对一个const char&赋值,因为被赋值的对象是const版本的operator[]函数的返回值。