c/c++语言中的const详解

const关键字在C语言中就有了,并不是C++新引入的。C++只是扩展了const的含义和用法。所以,这里我们分成C语言中的const和C++语言中的const两个部分来介绍。注意到,一般来说,C++语言中可以兼容C语言的用法,若是有例外,会特意标明。
  
  一. C语言中的const
  
  简言之,const关键字是一个修饰字,表明所修饰的变量是只读的,即只读变量(readonly)。注意区分只读变量与常量,它们是不同的。
  
  通过下面这段代码能看出区别:
  #define MAX (100) //MAX是通过预编译宏定义的常量
  const int Max=100; // Max是只读变量
  int a[MAX]; // 正确
  int b[Max]; // 编译报错,因为静态数组的长度必须是常量
  
  C语言中引入const关键字的目的:
  我觉得目的很简单,就是为了适应只读这个概念。这在后面的用法中能体会出来。而一些网上的参考资料中,在说C语言中的const时,就将其与#define预编译宏做比较,认为用const能“节省空间,避免不必要的内存分配,同时提高效率”。我认为这种说法是不准确的。首先,就C语言中的const来说,并不能真正替换#define宏常量,这从上面的代码中就能看到。即使在两者都可以使用的上下文环境中,例如以下代码:
  1 #difine M 3 //宏常量
  2 const int N=5;//编译至此,并不分配内存给N
  3 int i = N; //N的首次使用,编译至此,静态数据区分配内存存放N,栈数据区分配内存存放i,并完成赋值(初始化)
  4 int I = M; //预编译期间已替换成立即数,只需在栈数据区分配内存存放i,并完成赋值(初始化)
  5 int j = N; //N的再次使用,编译至此,只需在栈数据区分配内存给j并初始化,因为N在静态数据区已存在了
  6 int J = M; //预编译期间替换成立即数(与第4句的立即数只是值相同,实质是两个不相干的数),其他同第4句
  参考资料中认为,语句4和6在预编译期间进行宏替换需要分配内存,是没道理的,宏替换的本质是程序代码的替换,还没有进入编译环节,不存在内存分配一说。而认为语句5没有内存分配,也是不准确的。因为N确实不用再分配内存,但j作为栈变量,在定义的时候必然是要分配内存的。所以,从上面这6行代码来看:一、认为const节省空间,是指const定义的变量在静态数据区只有一份拷贝,而不像宏常量在预编译后散布在代码各处。这其实并不是真正的节省空间,更偏向于一种一致性的含义。二、认为避免了不必要的内存分配,就是无稽之谈了。const变量确实只要分配内存一次,但是宏常量在预编译后,就是立即数而已,根本不存在内存分配。三、认为能提高效率,这就更奇怪了。从编译效率来看,宏常量多了一步预编译,似乎效率低,但是考虑到真实程序中,即使你一个宏常量都不用,还是避免不了预编译这一步,因为还有其他宏呢,#include宏你不会不用吧。从运行效率来看,操作数是立即数,总归比操作数来自一个内存地址要快。所以所谓提高效率是不可能的了。
  
  C语言中const关键字用法:
  1.修饰一般变量
  一般常量是指简单类型的只读变量。这种只读变量在定义时,修饰符const 可以用在类型说明符前,也可以用在类型说明符后。例如:
  int const i=2; 或const int i=2;
  2.修饰数组
  定义或说明一个只读数组可采用如下格式:
  int const a[5]={1, 2, 3, 4, 5};或
  const int a[5]={1, 2, 3, 4, 5};
  3.修饰指针
  const int *p; // p 可变,p 指向的对象不可变
  int const *p; // p 可变,p 指向的对象不可变
  int *const p; // p 不可变,p 指向的对象可变
  const int *const p; //指针p 和p 指向的对象都不可变
  这里给出一个网上广为流传的记忆和理解的方法:
  先忽略类型名(编译器解析的时候也是忽略类型名),我们看const 离哪个近,离谁近就修饰谁。
  const int *p; //const 修饰*p,p 是指针,*p 是指针指向的对象,不可变
  int const *p; //const修饰*p,p 是指针,*p 是指针指向的对象,不可变
  int *const p; //const修饰p,p 不可变,p 指向的对象可变
  const int *const p; //前一个const 修饰*p,后一个const 修饰p,指针p 和p 指向的对象都不可变
  4.修饰函数的参数
  const 修饰符也可以修饰函数的参数,当不希望这个参数值被函数体内意外改变时使用。例如:
  void Fun(const int i); // 其实无意义,修饰指针才有意义
  告诉编译器i 在函数体中的值不能改变。
  5.修饰函数的返回值
  const修饰函数返回值其实用的并不是很多,它的含义和const修饰普通变量以及指针的含义基本相同。
  a.const int fun1() //这个其实无意义,因为参数返回本身就是赋值。
  b.const int * fun2() //调用时 const int *pValue = fun2(); 
   //我们可以把fun2()看作成一个变量,即指针内容不可变。
  c.int* const fun3() //调用时 int * const pValue = fun2(); 
   //我们可以把fun2()看作成一个变量,即指针本身不可变。
  
  二. C++语言对const的扩展
  
  首先,C++将const修饰的“只读变量”提升到了常量的高度。在C++中,定义常量已经被推荐放弃宏常量而用const了。也就是说,上面举例用的代码可以编译通过了:
  #define MAX (100) //MAX是通过预编译宏定义的常量
  const int Max=100; // Max在C++中是常量了
  int a[MAX]; // 正确
  int b[Max]; // 在C++中正确
  既然已经是常量了,那么,C++编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率提高。
  
  其次,因为C++中引入了引用类型,需要定义const和引用类型的搭配:
  int i;
  const int& ri = i; //对基本数据类型的const引用,表明使用引用时,不可修改应用目标
  A f;
  const A& rf = f; //对类或者结构类型的const引用,表明使用引用时,只能访问声明为const的成员
  引用类型常用在函数入参中,const引用在函数入参中也扮演重要角色:
  void function(const Class& Var); //引用参数在函数内不可以改变
  void function(const int& Var); //引用参数在函数内为常量不可变
  这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本, 然后传递过去,而它直接传递地址,所以这种传递比按值传递更有效.
  
  最后,也是最重要的,是在类中扩展const的使用。
  1.const修饰成员变量
  const修饰类的成员函数,表示成员常量,不能被修改,同时它只能在初始化列表中赋值。
   class A
   { 
   …
   const int nValue; //成员常量不能被修改
   …
   A(int x): nValue(x) { } ; //只能在初始化列表中赋值
   } 
  另外一种初始化常量成员的方法,是static和const并用。然后在类声明的外部初始化。
   class A 
   { 
   public: A() {} 
   private: static const int i; //注意必须是静态的! 
   }; 
   ...
   const int A::i=3;
  
  
  2.const修饰成员函数
  const修饰类的成员函数,则该成员函数不能修改类中任何成员,不能访问类中任何非const函数。一般写在函数的最后来修饰。
   class A
   { 
   …
   void function()const; //常成员函数, 它不改变对象的成员变量. 
   //也不能调用类中任何非const成员函数。
   } 
  任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其他非const成员函数,编译器将报错,这大大提高了程序的健壮性。
  另外,因为对于const类对象/指针/引用,只能调用类的const成员函数,因此,const修饰成员函数的最重要作用就是限制对于const对象的使用。
  3.const修饰类对象/对象指针/对象引用
  ·const修饰类对象表示该对象为常量对象,其中的任何成员都不能被修改。对于对象指针和对象引用也是一样。
  ·const修饰的对象,该对象的任何非const成员函数都不能被调用,因为任何非const成员函数会有修改成员变量的企图。
  例如:
  class AAA
  { 
   void func1(); 
  void func2() const; 
  } 
  const AAA aObj; 
  aObj.func1(); ×
  aObj.func2(); 正确
  
  const AAA* aObj = new AAA(); 
  aObj-> func1(); ×
  aObj-> func2(); 正确
  
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值