我们都知道,在标准C++中可以用const关键字来定义一个常类型的量或者是对象。用const定义的常类型的量或对象的值是不能被更新的, 因此,定义或说明常类型的量或对象时必须进行初始化。 其实,const在C++中是一个很有用的关键字,下面总结一下它的用法。
在c语言中,我们一般是用 #define
来声明常量的,它是一种编译预处理,给人的感觉好像不是语言的一部分。
而在C++中,我们完全可以用 const
代替 #define
,即“尽量用编译器而不用预处理”,const
有很多特性,使其优于 #define
。
const
可以放在类型说明符前,也可以放在类型说明符后,即语句const int k=10
和语句int const k=10
是等效的,都表示k是一个int型的常量10。
使用const
修饰指针时,由于const
的位置不同,而含意不同。下面举两个例子以说明它们的区别。
下面定义了—个指向字符串的常量指针:
1 2 3 4 5 | char arr1[]="Hello const!"; char arr2[]="Yes,I am fine"; char * const pstr=arr; |
其中,pstr是个常量指针。因此,下面赋值是非法的:
1 | pstr=arr2; |
而下面的赋值是合法的:
1 | *pstr='h'; |
因为指针pstr所指向的变量是可以更新的,不可更新的是常量指针pstr本身,即它所指的方向(别的字符串)。
下面定义了一个指向字符串常量的指针:
1 | const char *pstr2=arr1; //和char const * pstr2=arr1等效 |
其中,pstr2是—个指向字符串常量的指针。pstr2所指向的字符串不能更新,而pstr2本身是可以更新的。因此语句*pstr2='h'
是非法的,而语句pstr2=arr2
是合法的。
所以,在使用const修饰指针时,应该注意const的位置。定义一个指向字符串的指针常量时,const放在星号的右边;定义—个指向字符串常量的指针时,const放在星号的左边(char的前面,或者char与星号之间)。
使用const修饰符也可以说明引用,被说明的引用为常引用,该引用所引用的对象不能被更新。其定义格式如下:
1 | const <类型说明符> & <引用名> |
例如:
1 | const double & d |
在实际应用中,常指针和常引用往往用来作函数的形参,这样的参数称为常参数。使用常参数则表明该函数不会更新该常参数所指向或所引用的对象,非常适用于使用引用传递参数但在函数体内又不改变该参数的场合。
使用const关键字进行说明的成员函数,称为常成员函数。常成员函数有两个作用:一是可以用来操作常对象,只有常成员函数才有资格操作常对象,没有使用const关键字说明的成员函数不能用来操作常对象。二是限制成员函数,使其不能修改本对象的成员变量。
常成员函数说明格式如下:
1 | <类型说明符> <函数名>(<参数表>) const |
其中,const是加在函数说明后面的类型修饰符,它是函数类型的—个组成部分。因此,在函数实现部分也要带const关键字。下面举例说明了常成员函数的特征。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include <iostream> using namespace std; class test { private: int r1, r2; public: test( int a, int b ) { r1=a, r2=b; } void display(); void display() const; int add() const; int mul(); }; void test::display() { cout<<r1<<'#'<<r2<<endl; } void test::display() const { cout<<r1<<'&'<<r2<<endl; } int test::add() const { return ( r1 + r2 ); } int test::mul() { return ( r1 * r2 ); } int main() { test t1( 2, 10 ); t1.display(); cout<<"t1:r1+r2="<<t1.add()<<endl; cout<<"t1:r1*r2="<<t1.mul()<<endl; const test t2( 3, 17 ); t2.display(); cout<<"t2:r1+r2="<<t2.add()<<endl; //cout<<"t2:r1*r2="<<t2.mul()<<endl; 错误! system("pause"); return 0; } |
运行结果为:
1 2 3 4 5 | 2#10 t1:r1+r2=12 t1:r1*r2=20 3&17 t2:r1+r2=20 |
该程序中test类中声明了两个同名的display成员函数,但类型不同,相当于实现了函数重载,由const类型的成员函数来显示const类型的test对象t2。这里要注意的是: 只有常成员函数才有资格操作常对象,也就是说常对象只能调用常成员函数,不能调用非常成员函数(没有用const修饰的);但是,非常对象却能调用常成员函数 。因此,上例中t1.add()是对的,而t2.mul()是错误的。
类型修饰符const不仅可以修饰成员函数,也可以修饰数据成员,即常数据成员。由于const类型对象必须被切始化,并且不能更新,因此,在类中声明了const型数据成员时,只能通过成员初始化列表的方式来生成构造函数对常数据成员初始化。下面通过一个例子讲述使用成员初始化列表来生成构造函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #include <iostream> using namespace std; class test { private: const int a; const int &addr; static const int b; public: test(int i); void display(); }; const int test::b = 5; test::test(int i):a(i), addr(a){} void test::display(){ cout<<a<<':'<<addr<<':'<<b<<endl; } int main() { test t1(30); test t2(100); t1.display(); t2.display(); system("pause"); return 0; } |
运行结果:
1 2 | 30:30:5 100:100:5 |
在该程序中,设置了如下3个常类型数据成员:
1 2 3 | const int & addr; const int a; static const int b; |
其中,addr是常int型引用,a是int型常量,b是静态常int型变量。程序中对静态数据成员b进行初始化。值得注意的是构造函数的格式:
1 | test(int i):a(i),addr(a){} |
其中,冒号后面是一个数据成员初始化列表,它包含两个初始化项,用逗号隔开。 对于常类型的数据成员,必须采用这种方式初始化 。
const也可以用来修饰函数返回值,表明该返回值是不可修改的。事实上,函数返回值本来就不应该被修改(例如被赋值)。用const修饰函数返回值,就可以避免那些很蹩脚的人对函数返回值进行修改,尤其是在自定义数据类型的时候。
综上可见,const的使用可以使我们的程序更加安全可靠,可以将很多隐藏的错误交给编译器来帮你检查出来。因此,请在一切可以用const的地方用上const 。
本作品由 Yysfire 创作,采用进行许可。转载时请在显著位置标明本文永久链接:
http://yysfire.github.io/c_cpp/usage_summary_of_const_in_standard_c++.html