4.1.const的作用
(1)定义常量
(2)便于类型检查
const常量有数据类型修饰,而宏常量没有,const常量可以进行安全类型检查,如果缺少这一步,有可能发生异常错误。
(3)可以保护被修饰的东西
防止被意外修改。
(4)可以很方便的进行参数的修改
(5)为重载函数提供参考
1 class a {
2 void func() {};
3 //重载func(),不允许非成员函数在括号后使用const限定符
4 void func() const {};
5 };
(6)节省空间
const定义常量给出的是内存地址,程序运行过程只有一份拷贝,而宏定义在内存中有多份拷贝。
(7)提高效率
编译器不为普通的const变量分配存储空间,而是存在符号表中,编译期间没有读内存和存储操作,效率提高了。
4.2.const的使用
(1)默认状态下,const对象尽在文件内有效,如果你想在几个文件中共享,使用extern关键字吧!
(2)const的引用
1 int i = 5;
2 //允许绑定到非常量上
3 const int &r_1 = i;
4 //允许绑定到字面值常量
5 const int &r_2 = 10;
6 //允许绑定到double类型的引用
7 const double &r_3 = i;
8 //允许等号右侧有运算表达式
9 const int &r_4 = i + 5;
10 const double &r_5 = i+5;
一个常量引用绑定到另一种类型上发生了什么?
比如说第五行绑定到一个字面值常量上时候,编译器是这么做的:
1 int temp = 10;
2 const int &r_2 = temp;
首先把字面值常量用一个临时对象存储起来,所以说r_2绑定的是一个临时对象。C++ primer 5th中P62页的解释中,为什么如果不用const修饰上面第五行代码就会错误的原因就在此,它晦涩的指出了const限定符的作用其实是表明了此引用不会间接的改变被引用的对象,下面是一个说明:
1 int i = 5;
2 //允许绑定到非常量上
3 const int &r_1 = i;
4 //错误,不允许通过&r_1改变所绑定对象的值
5 &r_1++;
6 //正确,使用对象进行自身的操作
7 i++;
(3)指针和const
指向常量的指针:
1 double p = 3.14;
2 double p1 = 3.15;
3 //允许将一个普通变量取地址赋值给一个指向常量的指针,反之,不允许一个将一个常量取地址赋值给一个普通指针
4 double const *p_1 = &p;
5 double const *p_2 = &p1;
6 //正确,允许改变指针所指向的地址(改变指针本身)。
7 p_1 = p_2;
8 //错误,不允许改变指针所指地址存储的值
9 *p_1 = 50;
10 cout << *p_1;
11
常量指针:
double pi = 3.14;
double pi_1 = 3.16;
//不能够把一个常量取地址赋值给一个指针常量,因为显然等号右侧应该是一个可变的值
double * const pip = π
//允许通过指针修改所指向地址存储的值
*pip = 3.15;
//错误,不允许修改指针常量中指针本身
pip = &pi_1;
如何阅读一个定义?
在不带有数组的情况下,先读()的内容,然后从左往右读。比如说,double const *p_1 = &p; 先找到标识符p_1从左往右:*修饰,表明其首先为一个指针,然后是const修饰,说明这个指针所指的对象是一个常量,然后是基本类型符double,于是我们可以知道,这一条语句的定义理解为:我们定义了一个指向常量的指针p_1。
顶层const和底层const:
顶层const:表示指针本身是个常量,定义可以延申。
底层const:表示指针所指对象是一个常量,定义可以延申。
(4)const应用于函数
作为函数的形参:
1 //传递过来的参数在函数内不可以改变
2 void function(const int Var);
3 //参数指针所指内容为常量不可变
4 void function(const char* Var);
5 //参数指针本身为常量不可变
6 void function(char* const Var);
7 //引用参数在函数内不可以改变
8 void function(const int& Var);
修饰函数返回值:
1 //函数返回值不能作为左值
2 const int func();
3 //把function()看作一个变量,那么指针所指地址的对应的内容不可改变
4 const int * function();
5 //把function1()看作一个变量,那么指针本身不可以改变
6 int * const function1();
函数的返回值不能作为左值这一点,在操作符重载中有所体现,比如说,避免了等号左边出现表达式的情况。const引用做形参和返回值在这里不再赘述,留下来自己思考!
(5)类成员中的const
修饰类的变量:
1 class Test {
2 public:
3 //错误,const修饰的成员变量只能在初始化列表中赋初始值
4 Test(int i) {
5 value = i;
6 };
7 //正确
8 Test(int i) :value(i) {};
9 private:
10 const int value;
11 };
修饰成员函数:
1 class Test { 2 public: 3 Test(int i,int j) :value(i),p(&j) {}; 4 void test1(int q) const{ 5 //正确,const成员函数无法保证指针所指对象的改变 6 *p = NULL; 7 //错误,不允许在const成员函数内改变数据成员 8 value=5; 9 } 10 11 private: 12 int value; 13 int *p; 14 };
修饰类对象(普通变量,指针,引用)
1 class Test { 2 public: 3 Test(int i,int j) :value(i),p(&j) {}; 4 void test1() const{ 5 //正确,const成员函数无法保证指针所指对象的改变 6 *p = NULL; 7 8 } 9 void test2() { 10 11 } 12 void test3() const{ 13 //正确,允许访问const成员函数 14 test1(); 15 //错误,不允许访问非const成员函数 16 test2(); 17 } 18 19 private: 20 int value; 21 int *p; 22 }; 23 int main() 24 { 25 Test T(5,6); 26 const Test T1(6,7); 27 //正确,非const对象可以调用const成员函数 28 T.test3(); 29 //错误,const修饰的类对象是常对象只能调用const成员函数,因为任何非const成员函数都有修改成员变量的企图。 30 T1.test2(); 31 32 }