值替代 |
当用C语言进行程序设计时,预处理器可以不受限制地建立宏并用它来替代值。因为预处理器只做些文本替代,它既没有类型检查概念,也没有类型检查功能,所以预处理器的值替代会产生一些微小的问题,这些问题在C++中可以通过使用const值而避免。
比如:
#define BUFFERSIZE 100
BUFFERSIZE是一个名字,它只是在预处理期间存在,因此它不占用存储空间且能放在一个头文件里,目的是为使用它的所有编译单元提供一个值。使用值替代而不是使用所谓的“不可思议的数”,这对于支持代码维护是非常重要的。
头文件里的const
要使用const而非#define,同样必须把const定义放进头文件里。这样通过包含头文件,可把const定义单独放在一个地方并把它分配给一个编译单元。C++中的const默认为内部连接,也就是说,const仅在const被定义过的文件里才是可见的,而在连接时不能被其他编译单元看到。
可以参考:http://www.cnblogs.com/sheshiji/p/3427896.html
const可以用于数组,但必须保证编译器不会复杂到把一个数组保存到它的符号表中,所以必须分配内存。在这种情况下,const意味着“不能改变的一块存储空间”。然而,不能在编译期间使用它的值,因为编译器在编译期间不需要知道存储的内容。这样,就能明白下面的代码是非法的:
const int i[] = { 1,2,3,4 }; float f[i[3]]; // illegal
在一个数组定义里,编译器必须能产生这样的代码,它们移动栈指针来存储数组。在上面的非法定义里,它不能在数组定义里找到一个常数表达式。
与C语言的区别
C中const的意思是“一个不能被改变的普通变量”,const常量总是占用存储而且它的名字是全局的。这样,C编译器不能把const看成一个编译期间的常量。在C中,如果写:
const int bufsize = 100; char buf[bufsize];
尽管看起来好像做了一件合理的事,但这将得出一个错误。因为bufsize占用某块内存,所以C编译器不知道它在编译时的值。在C语言中可以选择这样书写:
const int bufzie;
这样写在C++中是不对的,而C编译器则把它作为一个声明,指明在别的地方有存储分配。因为C默认const是外部连接的,所以这样做是合理的。C++默认const是内部连接的,这样如果在C++中想完成与C中同样的事情,必须用extern明确地把连接改为外部连接:
extern const int bufsize; // Declaration only
在C中使用限定符const不是很有用的,C总是迫使程序员在预处理器里使用#define。
指针 |
当使用带有指针的const时,有两种选择:const修饰指针指向的内容,或者const修饰在指针里存储的地址(即修饰的是变量)。
指向const内容的指针
const修饰“最靠近”它的那个。这样,如果要使正指向的元素不发生改变,得写一个像这样的定义:
const int *u;
从标识符开始,是这样读的:u是一个指针,它指向一个const int。这里不需要初始化,因为u可以指向任何标识符(也就是说变量u不是一个const),但它所指的值是不能改变的。
int const *v;
并非所有的人都很肯定地认为:应该读成,“v是一个指向int的const指针”。然而,实际应读成“v是一个指向恰好是const的int的普通指针”,效果与前面定义的一样。为使程序更具有可读性,应该坚持用第一种形式。
const指针变量
使指针本身成为一个const指针,必须把const标明的部分放在*的右边,如:
int d = 1; int *const w = &d;
现在它读成“w是一个指针,这个指针是指向int的const指针”。因为指针本身现在是const指针,编译器要求给它一个初始值,这个值在指针生命周期内不变。然而要改变它所指向的值是可以的,可以写:
*w = 2;
字符数组的字面值
有人可以写:
char *cp = “howdy”;
编译器将接受它而不报告错误。从技术上讲,这是一个错误,因为字符数组的字面值(在这里是”howdy”)是被编译器作为一个常量字符数组建立的,所引用该字符数组得到的结果是它在内存里的首地址。修改该字符数组的任何字符都会导致运行时错误。
如果要修改字符串,就要把它放在一个数组中:
char cp[] = “howdy”;
函数参数和返回值 |
返回const值
对于内部类型来说,按值返回的是否是一个const,是无关紧要的,所以按值返回一个内部类型时,应该去掉const,从而不使客户程序员混淆。
当处理用户定义的类型时,按值返回常量是很重要的。如果一个函数按值返回一个类对象为const时,那么这个函数的返回值不能是一个左值(即它不能被赋值,也不能被修改),如:
class X { int i; public: X(int ii = 0); void modify(); }; X::X(int ii) { i = ii; } void X::modify() { i++; } X f5() { return X(); } const X f6() { return X(); } void f7(X &x) { x.modify(); } int main() { f5() = X(1); // OK f5().modify(); // OK //! f7(f5()); // causing warning // Causes complie-time errors //! f6() = X(1); //! f6().modify(); //! f7(f6()); return 0; }
f5()返回一个非const X对象,然而f6()返回一个const X对象。仅仅是非const返回值能作为一个左值使用,因此,当按值返回一个对象时,如果不让这个对象作为一个左值使用,则使用const很重要。
当按值返回一个内部类型时,const没有意义的原因是:编译器已经不让它成为一个左值(因为它总是一个值而不是一个变量)。仅当按值返回用户定义的类型对象时,才会出现上述问题。
传递和返回地址
const int *const w() { static int i; return &i; } int *ip2 = w(); // Not OK const int *const ccip = w(); // OK const int *cip2 = w(); // OK
编译器拒绝把函数w()的返回值赋给一个非const指针,而接受一个const int* const,但令人奇怪的是它也接受一个const int *,这不是与返回类型恰好匹配的。因为这个值(包含在指针中的地址)正被拷贝,所以自动保持这样的约定:原始变量不能改变。
类 |
const对象只能调用const成员函数。
const成员函数可以被const对象和非const对象调用。不修改数据成员的任何函数都应该把它们声明为const,这样它可以和const对象一起使用。
可变的:按位const和按逻辑const
如果想要建立一个const成员函数,但仍然想在对象里改变某些数据,这时该怎么办呢?下面是一个例子:
class Y { int i; public: Y(); void f() const; }; Y::Y() { i = 0; } void Y::f() const { //! i++; // Error -- const member function ((Y *)this)->i++; // OK: cast away const-ness // Better: use C++ explicit cast syntax: (const_cast<Y *>(this))->i++; } int main() { const Y yy; yy.f(); // Actually changes it! }
这种方法是可行的,在过去的程序代码里可以看到这种用法,但这不是首选的技术。应当在类声明里使用关键字mutable,以指定一个特定的数据成员可以在一个const对象里被改变。