C++中用const限定符来定义常量,但const出现的可以出现在常量定义中,也可以出现在方法定义中,并且出现的位置也有所不同,所表示的意思也不同。
在任何可能的情况下都要使用 const。在声明的变量或参数前加上关键字 const 用于指明变量值不可被篡改 (如 const int foo ). 为类中的函数加上 const 限定符表明该函数不会修改类成员变量的状态 (如 class Foo { int Bar(char c) const; };).
但是,const 是入侵性的: 如果你向一个函数传入 const 变量, 函数原型声明中也必须对应 const 参数 (否则变量需要 const_cast 类型转换), 在调用库函数时显得尤其麻烦。关键字 mutable 可以使用, 但是在多线程中是不安全的, 使用时首先要考虑线程安全.
const 的位置:
有人喜欢 int const *foo 形式, 不喜欢 const int* foo, 他们认为前者更一致因此可读性也更好: 遵循了 const 总位于其描述的对象之后的原则。但是一致性原则不适用于此, “不要过度使用” 的声明可以取消大部分你原本想保持的一致性. 将 const 放在前面才更易读, 因为在自然语言中形容词 (const) 是在名词 (int) 之前.不强制 const 在前. 但要保持代码的一致性!
1. 定义 const 对象
因为常量在定义后就不能被修改,所以定义时必须初始化:
const int bufSize = 512; // ok: initialize
2 . C与C++中const的区别
c++中,const 对象默认为文件的局部变量,也就是在C++中const变量的链接性质是内部的,对于C++来说,const 变量是global namespace的全局变量,C++默认const变量的链接性质是内部的,而C中const变量的链接性质是外部的. 在C++中,const变量默认是内部链接的,除非你显式加上extern修饰词,否则,其它文件是看不到的。
// 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
// uses bufSize defined in file_1
for (int index = 0; index != bufSize; ++index)
3. 指针和 const 限定符
1)指向 const 对象的指针
允许把非 const 对象的地址赋给指向 const 对象的指针,不允许把一个 const对象的地址赋给一个普通的、非 const 对象的指针。
const double pi = 3.14;
double *ptr = π // const对象的地址不能赋给非const指针
const double *cptr = π // ok: cptr is a pointer to const
const void *cpv = π // ok: cpv is const
double dval = 3.14;
cptr = &dval; // ok: but can't change dval through cptr
可以通过赋值改变指针本身的值,但不能通过指针(eg:*p)改变指针指向的值。
char * str = (char*)malloc(sizeof(char)*10);
strcpy(str,"abcd");
const char* cpstr = str;// OK: cpstr = "abcd"
strcpy(str,"dcba"); // ok: cpstr = "dcba"
//strcpy(cpstr,"ddd"); //error
*cptr = 10.12; //Error
2)指向 const 对象的 const 指针
const 指针——本身的值不能修改, 但可以改变指针指向的值:
int errNumb = 0;
int *const curErr = &errNumb; // curErr is a constant pointer
*curErr = 10; //OK
3)指向 const 对象的 const 指针
const double pi = 3.14159;
const double *const pi_ptr = π
// pi_ptr is const and points to a const object
4)区别指向 const 对象的指针和const 指针
一个很好的方式就是,从定义的右边往左边读其意思来区别。比如const double *ptr 可 以读为ptr指针指向一个const double对象,
这样就知道是指向const对象的指针了;同样,int *const curErr 可以读为 curErr 是一个const型的指针,指向int对象。
5)指针和 typedef
先看下下面的代码,cstr1、cstr2、cstr3那些是const指针,那些是指向const对象的指针?
string s ;
typedef string *pstring;
const pstring cstr1 = &s; //const修饰的是pstring
pstring const cstr2 = &s; //const修饰的是pstring
string *const cstr3 = &s;
可能有人会误认为cstr1是指向const对象的指针,其实cstr1、cstr2、cstr3都是const指针。错误的原因在于将 typedef 当做文本扩展了。声明 const pstring 时,
const 修饰的是pstring的类型,这是一个指针。该声明语句应该是把 cstr1 定义为指向 string 类型对象的 const 指针,这个定义等价于cstr3的声明定义。
4. const 形参
在调用函数时,如果该函数使用非引用的非 const 形参,则既可给该函数传递 const 实参也可传递非 const 的实参。即可用 const 对象初始化非 const 对象,反之亦然。如果将形参定义为非引用的 const 类型,则在函数中,不可以改变实参的局部副本。由于实参仍然是以副本的形式传递,因此传递给 fcn 的既可以是 const 对象也可以是非 const 对象。如果使用引用形参的唯一目的是避免复制实参,则应将形参定义为 const 引用。
// compare the length of two strings
bool isShorter(const string &s1, const string &s2){
return s1.size() < s2.size();
}
注意:调用非 const 引用形参的函数时,传递一个右值或具有需要转换的类型的对象同样是不允许的:
int incr(int &val) {
return ++val;
}
int main() {
short v1 = 0;
const int v2 = 42;
int v3 = 11
v3 = incr(v1); // error: v1 is not an int
v3 = incr(v2); // error: v2 is const
v3 = incr(0); // error: literals are not lvalues error C2664: cannot convert parameter 3 from 'int' to 'int &'
v3 = incr(v1 + v2); // error: addition doesn't yield an lvalue
int v4 = incr(v3); // ok: v3 is a non const object type int
}
问题的关键是非 const 引用形参只能与完全同类型的非 const 对象关联。
总之,应该将不需要修改的引用形参定义为 const 引用。普通的非 const 引用形参在使用时不太灵活。这样的形参既不能用 const 对象初始化,
也不能用字面值或产生右值的表达式实参初始化。
5. const 对象
指向 const 对象的指针或引用只能用于调用其 const 成员函数,如果尝试用它们来调用非 const 成员函数,则是错误的。