C++ 中的 CONST 用法简介
1、 定义 非指针 常量
const int nPI = 3.14159;
此用法常用来替代#define 预定义语句。
C++的编译器通常不为普通 const 常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作, 使得它的效率也很高,同时,这也是它取代预定义语句的重要基础。
const定义会由编译器对它进行类型的检测,消除了预定义语句的隐患。
2、 修饰指针变量
char * pa = "aaa" ;
const char * p1 = "111" ; // *p1 不可变, p1 可变
char const * p2 = "222" ; // 与前一句等价
char * const p3 = "333" ; // *p3 可变, p3 不可变
const char * const p4 = "444" ; // p4 与 *p4 都不可变
* pa = 'b' ; // OK
//*p1 = 'b'; // error C3892: 'p1' : you cannot assign to a variable that is const
p1 = pa ; // OK
//*p2 = 'b'; // error C3892: 'p2' : you cannot assign to a variable that is const
p2 = pa ; // OK
* p3 = 'b' ; // OK
//p3 = pa; // error C3892: 'p3' : you cannot assign to a variable that is const
//*p4 = 'b'; // error C3892: 'p4' : you cannot assign to a variable that is const
//p4 = pa; // error C3892: 'p4' : you cannot assign to a variable that is const
如果 const 位于星号的左侧,则 const 就是用来修饰指针所指向的变量,即指针指向为常量;
如果 const 位于星号的 右侧, const 就是修饰指针本身,即指针本身是常量。
3、 修饰引用变量
int nValue = 100;
int & nRef = nValue ;
const int & nA = nValue ; // 常量引用,不能通过 nA 改变 nValue 的值
int const & nB = nValue ; // 同上 ,这两种用法基本上是在传递参数的时候出现
int & const nC = nValue ; // 引用必须在定义的时候初始化,因此这个用法意义不大。
// nA = 101; // error C3892: 'nA' : you cannot assign to a variable that is const
// nB = 102; // error C3892: 'nB' : you cannot assign to a variable that is const
4、 修饰函数参数
void Func ( const T tValue );
void Func ( const T & tRefValue );
void Func ( const T * tpValue );
此时 const限定函数参数在函数体内不会被修改。 void Func ( const T tValue );
这个方式对于非内部数据对象不建议适用,效率比较低。对于内部对象可以适用,目的是限制在函数内部对参数变量值的修改,但是滥用了对代码可阅读性有影响。
对于非内部数据类型的参数而言,象 void Func( T tValue ) 这样声明的函数注定效率比较 低 。因为函数体内将产生 T 类型的临时对象用于复制参数 tValue ,而临时对象 拷贝 构造 、 析构过程 将加大运行开销 。
用c onst 引用对象作为函数参数 , 避免了产生 T 类型 临时对象的开销 , 同时也可以限制 调用函数 ,使其不 能修改 引用 对象的值。同理,将指针参数声明为const ,函数 中 将不 能 修改由这个参数所指 向 的对象。 这样就既可以提高效率,也能限制对象值不被修改。
对于内部数据类型来说,函数参数传引用与传值的效率几乎一样,对于内部数据类型就没有必要用 const 修饰的 传引用或指针的方式传递参数。
5、 修饰函数返回值
const T Func ();
const T & Func ();
const T * Func ();
此时 返回值也要相应的 赋值 给一个常量 引用 或常 量 指针 ,防止对函数返回值的修改。
上面列出的第一种方式,当 T为内部数据类型时, const 修饰就没有必要存在。如果非要写上去,编译器也不会说你错了。
6、 修饰类成员函数
class T
{
public :
T ( int nValue ){ m_nValue = nValue ; m_nMutableValue =0; }
int GetValue () const
{
++ m_nMutableValue ;
// ++m_nValue; // error C2166: l-value specifies const object
return m_nValue ;
}
void SetValue ( int nValue ){ m_nValue = nValue ;}
int Test (){ return GetValue ();}
int m_nValue ;
mutable int m_nMutableValue ;
};
int main ()
{
const T test (100);
test . GetValue ();
test . SetValue (101); // error C2662: 'T::SetValue' : cannot convert 'this' pointer from 'const T' to 'T &'
return 0;
}
当需要定义一个 const 修饰的类对象时,该对象调用的 成员 函数就必须定义为 const 成员 函数。 非 const 成员函数不能被常量对象调用,因为它可能企图修改常量的数据成员 。
平时 定义的类的成员函数中,常常有一些成员函数不 会去 改变类 成员变量的值 , 换句话说 ,这些 成员 函数是" 只读 " 函数,而有一些函数 则 要修改类 成员变量 的值。如果把不改变数据成员的函数都加上 const 关键字进行标识,显然,可提高程序的可读性。其 次 ,它还能提高程序的可靠性 : 已定义成 const 的成员函数,一旦企图修改数据成员的值,则编译器 会报错。 特殊情况,就是用 mutable 关键字修饰过的成员变量可以在声明为 const 成员 函数中被改变。
另外, const 成员函数中只可以调用类的 const 成员函数,不能调用类的非 const 成员函数,如果调用了,编译器就不乐意了(报错!)。但是类的非 const 成员函数可以调用类的 const 成员函数。
7、 const修饰类成员变量
class T
{
public :
T ( int nValue ): m_nValue ( nValue ){}
const int m_nValue ;
};
const 类成员变量 只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其 const 类成员变量 的值可以不同。所以不能在类声明中初始化 const 数据成员 。类的 const 成员变量需要在构造函数初始化列表中初始化。
8、 const 与函数重载
class T
{
public :
void Test ( const int ){}
//void Test(int){}; // error C2535: 'void T::Test(const int)' : member function already defined or declared 。该函数与第一个函数等价
void Test ( const int &){}
void Test ( int &){}
void Test ( const int &) const {}
void Test ( int & nValue ) const {++ nValue ;}
};
const 与函数重载需要注意以下几点:
A)参数为非引用的 const 和 non-const 不能构成重载;
B)参数为 non-const 引用和 const 引用可以构成重载( non-const 引用和 non-const 非引用也可以构成重载);
C)调用具有二义性,所以重载的时候最好还是老老实实的不要搞那么花样。
以上内容主要针对C++中的const,部分在C中也适用,但是C中const的用法还有些不同的地方,因本人接触的少,就不啰嗦了。文中提及的编译错误均为VC9.0中的编译错误信息。