const 是constant 的缩写,“恒定不变”的意思。被const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。所以很多C++程序设计书籍建议:“Use const whenever you need”。
- const常量
- 修饰函数参数、返回值,甚至函数的定义体
1、用const 修饰函数的参数
(1)用函数参数进行返回值的:不用const修饰,否则“指针传递/引用传递”都不能正确返回参数值;
(2)函数参数作输入:
- 输入参数采用“指针传递”,加const 修饰可以防止意外地改动该指针,起到保护作用。
例如StringCopy 函数:
void StringCopy(char *strDestination, const char *strSource);
其中strSource 是输入参数,strDestination 是输出参数。给strSource 加上const修饰后,如果函数体内的语句试图改strSource 的内容,编译器将指出错误。
- 输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const 修饰。
不要将函数void Func1(int x) 写成void Func1(const int x)
不要将函数void Func2(A a) 写成void Func2(const A a)
关于内部和非内部数据类型参数传递:
- 对于非内部数据类型的参数而言,void Func2(A a)效率低,因为调用时,需要产生对实参赋值,产生一个临时对象,临时对象构造、拷贝、析构耗时,可改成void Func(A &a);而引用可能会修改a,故改成void Func(const A &a)。
- 对于内置数据类型,没有必要写成void Func(const int &x),因为内部数据类型的参数不存在构造、析构的过程,复制也非常快,“值传递”和“引用传递”的效率几乎相当。
“const &”修饰输入参数的用法:
- 对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。
- 对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x) 不应该改为void Func(const int &x)。
2、用const 修饰函数的返回值
(1)如果给以“指针传递”方式的函数返回值加const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
例如函数:const char * GetString(void);
如下语句将出现编译错误:char *str = GetString();
正确的用法是:const char *str = GetString();
(2)如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。
例如不要把函数int GetInt(void) 写成const int GetInt(void)。
同理不要把函数A GetA(void) 写成const A GetA(void),其中A 为用户自定义的数据类型。
- 如果返回值不是内部数据类型,将函数A GetA(void) 改写为const A & GetA(void)的确能提高效率。
- 此时千万千万要小心,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了,否则程序会出错。
函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。
示例:
class A { A & operate = (const A &other); // 赋值函数}; A a, b, c; // a, b, c 为A 的对象a = b = c; // 正常的链式赋值(a = b) = c; // 不正常的链式赋值,但合法
解答:
- 如果将赋值函数的返回值加const 修饰,那么该返回值的内容不允许被改动。
- 上例中,语句 a = b = c 仍然正确,但是语句 (a = b) = c 则是非法的,因为a=b调用重载函数运算,a=b是成了const对象。
3、const 成员函数(const的作用:说明其不会修改数据成员)
任何不会修改数据成员的函数都应该声明为const 类型。如果在编写const 成员函数时,不慎修改了数据成员,或者调用了其它非const 成员函数,编译器将指出错误,这无疑会提高程序的健壮性。
示例:
以下程序中,类stack 的成员函数GetCount 仅用于计数,从逻辑上讲GetCount 应当为const 函数。编译器将指出GetCount 函数中的错误。
class Stack { public: void Push(int elem); int Pop(void); int GetCount(void) const; // const 成员函数private: int m_num; int m_data[100]; }; int Stack::GetCount(void) const{ ++m_num; // 编译错误,企图修改数据成员m_num Pop(); // 编译错误,企图调用非const 函数 return m_num; }
const 成员函数的声明看起来怪怪的:const 关键字只能放在函数声明的尾部,大概是因为其它地方都已经被占用了。
关于const函数的几点规则:
- const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数。
- (因为const对象不能被修改,与const成员函数一致;而非const对象可以修改/不修改)
- const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的。
- const成员函数不可以修改对象的数据,不管对象是否具有const性质,它在编译时,以是否修改成员数据为依据进行检查。
- 加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的。
const function使用实例:
(1)const 函数只能调用 const 函数,即使某个函数本质上没有修改任何数据,但没有声明为const,也是不能被const函数调用的。
示例1:
class my { public: string& operator[](const string& s) const { return table[s]; } //编译错误private: map table; };
解答:
当本重载函数声明为const后,就只能调用其他的同样也声明为const的函数;而table[s]实际上调用的是map的下标重载运算函数,该函数并没有声明为const。所以去掉本函数的const声明,就可以顺利通过编译了。
示例2:
class A{ public: A(int n) { num = n; } void incr() { num += 5; } void disp() const { cout << numspan> int times(int m) { return num * m; } int f() const { incr();//passing `const A' as `this' argument of `void A::incr()' discards qualifiers disp();// ok times(2);//passing `const A' as `this' argument of `int A::times(int)' discards qualifiers return num; } private: int num; }; int main(int argc, char *argv[]) { A a(5); a.f(); system("PAUSE"); return EXIT_SUCCESS; }
示例3:
DateTime类是自定义的,isvalid是DateTime类的方法。
bool DateTime::earlier(const DateTime &dt){ if(!isvalid()) return true; if(!dt.isvalid()) return false; //出错,提示 The non-const member function "DateTime::isvalid()" is called for "const DateTime"}
解答:
- 每个非static非const成员函数都有一个隐含的this指针,是非const型的,不能接受const型实参;
- 在你的函数里,dt是const DateTime &型的,dt.isvalid()相当于将const对象传给非const的this指针,所以出错。
- 如果isvalid不改变对象状态,可以将earlier成员函数定义为const型。
如果用const来修饰函数,那么函数一定是类的成员函数。
const 类型的成员函数不能返回非const类型的引用。
示例:
class Test { public : int & GetValue()const; private: int value; }; int &Test::GetValue() const{ return value; //value此时具有const属性,与返回值类型int &的非const属性不匹配}
解答:
- 这样的代码在vs2003中提示的错误:error C2440: “return” : 无法从“const int”转换为“int &”。
- 在const函数中传递this的时候把this变成了const T* const this,所以一个非const的引用指向一个const类型的变量,就会error。
修改:
- 把int value 改成mutable int value,mutable修饰的变量使之在const函数中可以被改变的。
- return value 改成。 return const_cast(value)。const_cast去掉了const性质。
- 把函数写成const int &Test::GetValue() const ,.这样做的目的是使引用的变量也是const类型的,就相当于const int & b 。
- 把引用去掉,写成返回值类型的。
- 把函数后面的const去掉。
- 返回值不是类的成员变量。
int &Test::GetValue() const{ int temp = value; return temp; }
cosnt函数(const放在函数后面):
在调用成员函数时,对象的传递是通过this隐藏的,const实际上修饰了this指向的对象,假如定义了:
int &Test::GetValue() const{ int temp = value; return temp; }
- f函数其实有两个参数,第一个是A * const this(this指向的对象是不能改变的) 另一个才是int类型的参数。
- 如果我们不想f函数改变参数的值,可以把函数原型改为f(const int),但如果我们不允许f改变this指向的对象呢?因为this是隐含参数,const没法直接修饰它,就加在函数的后面了,表示this的类型是const A *const this。
- const修饰*this是本质,至于说“表示该成员函数不会修改类的数据,否则会编译报错”之类的说法只是一个现象,根源就是因为*this是const类型的。
在C中,常量只的是字面值,如0x1,1L等,const称为只读常量。
const声明的变量在栈里面,只是其内容不能在代码中改变,它不是在静态内存中的,访问它的时候弹栈。访问cosnt变量时,不能像真正的常量一样从一个已知的固定内存地址中取得,这对早期的C语言编译器是不可能的。由于const是定义在栈里面的,它在程序运行时才能确定里面的值是多少,所以在编译的时候不能用它来定义数组大小了 。
转载自:http://blog.csdn.net/zcf1002797280/article/details/7816977(侵权即删)