概念

常量是存放固定且不可变值的,一旦确定初始值则在程序其它地方不可改变, 所以const对象必须初始化。常量一般使用const关键字来修饰。

const 对象可以大致分为三类:

1. const int a

const int a =10;

int const b =10;

这两种格式是完全相同的。也就是说const 与int哪个写前都不影响语义。有了这个概念后,我们来看这两个家伙:const int * pi与int const * pi ,它们的语义有不同吗?

你只要记住一点,int 与const 哪个放前哪个放后都是一样的,就好比const int n;与int const n;一样。也就是说,它们是相同的。

2. const int * p

前面已经说了 const int * p与int const * p 是完全一样的。

我们根据下面的例子看看它们的含义。

int a =30; 
int b =40; 
const int * p=&a; 
p=&b;     //注意这里,p可以在任意时候重新赋值一个新内存地址 
b=80;      //想想看:这里能用*pi=80;来代替吗?当然不能 
printf( “%d”, *p ) ;   //输出是80

语义分析:

p的值是可以被修改的。即它可以重新指向另一个地址的,但是,不能通过*p来修改b的值。

首先const  修饰的是整个*p(注意,是*p而不是p)。所以*p(p指向的对象)是常量,是不能被赋值的(虽然p所指的b是变量,不是常量)。 
其次,p前并没有用const 修饰,所以p是指针变量,能被赋值重新指向另一内存地址的。

你可能会疑问:那又如何用const 来修饰pi呢?其实,你注意到int * const pi中const 的位置就大概可以明白了。请记住,通过格式看语义。 我们看看下面的定义。

3. int* const p

这里的const修饰的p,而不是上面的*p,我们根据下面的例子来分析:

int a=30; 
int b=40; 
int * const p=&a; 
//p=&b;     
注意这里,p不能再这样重新赋值了,即不能再指向另一个新地址。 
b=80;     
//这里能用*p=80;来代替吗?可以,这里可以通过*p修改a的值。 
//请自行与前面一个例子比较。 
printf( “%d”, *p ) ;   //输出是80

*p=100;

printf( “%d”, a ) ;   //输出是100

语义分析: 
     
p值是不能重新赋值修改了。它只能永远指向初始化时的内存地址了。并且可以通过*p来修改a的值了。与前一个例子对照一下吧!看以下的两点分析

1). p因为有了const 的修饰,所以只是一个指针常量:也就是说p值是不可修改的(即p不可以重新指向b这个变量了)。

2). 整个*p的前面没有const 的修饰。也就是说,*p是变量而不是常量,所以我们可以通过*p来修改它所指内存a的值。

总之一句话,这次的p是一个指向int变量类型数据的指针常量。 
最后总结两句: 
     1).如果const 修饰在*p前则不能改的是*p而不是指p
     2).如果const 是直接写在p前则p不能改。

4.补充三种情况。

这里,我再补充以下三种情况。其实只要上面的语义搞清楚了,这三种情况也就已经被包含了。不过作为三种具体的形式,我还是简单提一下吧!

情况一:int * pi指针指向const int n常量的情况 
const int n1=40; 
int *pi; 
pi=&n1;//这样可以吗?不行,VC下是编译错。const int 类型的n1的地址是不能赋值给指向int类型地址的指针pi的。否则pi岂不是能修改n1的值了吗!   
pi=(int* ) &n1;  // 这样可以吗?强制类型转换可是C所支持的。 VC下编译通过,但是仍不能通过*pi=80来修改n1的值。去试试吧!看看具体的怎样。

情况二:const int * pi指针指向const int n1的情况
const int n1=40; 
const int * pi; 
pi=&n1;//两个类型相同,可以这样赋值。n1的值无论是通过pi还是n1都不能修改的。

情况三:用const int * const pi申明的指针 
int n; 
const int * const pi=&n; 
//你能想象pi能够作什么操作吗?pi值不能改,也不能通过pi修改n的值。因为不管是 
//*pi还是pi都是const的。

5. 常量引用

int a = 10;

int& p1 = a;//正确

int& p2 = 2;//错误,需要引用左值

const int& p3 = a;//正确

const int& p4 = 4;//正确

关于引用的初始化有两点值得注意:

(1)当初始化值是一个左值(可以取得地址)时,没有任何问题,可以用常量引用也可以用非常量引用, 如p1,p3;

(2)当初始化值不是一个左值时,则只能对一个const T&(常量引用)赋值。如p2,p3。而且这个赋值是有一个过程的:

首先将值隐式转换到类型T,然后将这个转换结果存放在一个临时对象里,最后用这个临时对象来初始化这个引用变量。

如果是对一个常量进行引用,则编译器首先建立一个临时变量,然后将该常量的值置入临时变量中,对该引用的操作就是对该临时变量的操作。对常量的引用可以用其它任何引用来初始化;但不能改变。

6. 常量函数、常量引用参数、常量引用返回值

例1:bool verifyObjectCorrectness(const myObj &obj); //const reference parameter
例2:void Add(const int &arg) const; //const function
例3:IStack const & GetStack() const { return _stack; } //return const reference

  6.1 常量函数
  1. 一个函数通过在其后面加关键字const,它将被声明为常量函数
  2. 在C++,只有将成员函数声明为常量函数才有意义。带有const作后缀的常量成员函数又被称为视察者(inspector),没
         有const作后缀的非常量成员函数被称为变异者(mutator)
  3. 与const有关的错误总是在编译时发现
  4. [摘]If the function is not declared const, in can not be applied to a const object, and the compiler will  
         give an error message. A const function can be applied to a non-const object 
  5. 在C++中,一个对象的所有方法都接收一个指向对象本身的隐含的this指针;常量方法则获取了一个隐含的常量this指针
      void func_name() const;
      以上说明函数func_name()不会改变*this。当你把this指针看成函数func_name()的一个不可见参数就理解了
      void func_name(T *this) (no const)
      void func_name(const T *this) (const)
  6. 常量函数可以被任何对象调用,而非常量函数则只能被非常量对象调用,不能被常量对象调用,如:

  1. class Fred {
  2. public:
  3. void inspect() const; // This member promises NOT to change *this
  4. void mutate(); // This member function might change *this
  5. };
  6.  
  7. void userCode(Fred& changeable, const Fred& unchangeable)
  8. {
  9. changeable.inspect(); // OK: doesn't change a changeable object
  10. changeable.mutate(); // OK: changes a changeable object
  11.  
  12. unchangeable.inspect(); // OK: doesn't change an unchangeable object
  13. unchangeable.mutate(); // ERROR: attempt to change unchangeable object
  14. }

 7. 在类中允许存在同名的常量函数和非常量函数,编译器根据调用该函数的对象选择合适的函数
    当非常量对象调用该函数时,先调用非常量函数;
    当常量对象调用该函数时,只能调用常量函数;
    如果在类中只有常量函数而没有与其同名的非常量函数,则非常量与常量对象都可调用该常量函数;如:

  1. #include <iostream>
  2. using namespace std;
  3.  
  4. struct A
  5. {
  6. void f() const { cout<<"const function f is called"<<endl; }
  7. void f() { cout<<"non-const function f is called"<<endl; }
  8. void g() { cout<<"non-const function g is called"<<endl; }
  9. };
  10.  
  11. void main()
  12. {
  13. A a;
  14. const A& ref_a = a;
  15. a.f(); //calls void f(),输出:non-const function f is called
      ref_a.f();//calls void f () const, 输出:const function f is called
  1.   a.g(); //ok, normal call
      ref_a.g(); //error, const object can not call non-const function }

  8. const关键字不能用在构造函数与析构函数中。因为构造函数的目的是初始化域值,因此它必须更改对象,析构函数同理

  6.2 常量引用参数

  本例中,一个myObj类型的对象obj通过引用传入函数verifyObjectCorrectness。为安全起见,使用了const关键字来确保函数verifyObjectCorrectness不会改变对象obj所引用的对象的状态。此外,通过声明参数常量,函数的使用者可以确保他们的对象         不会被改变,也不必担心在调用函数时带来副作用。以下代码试图对声明为常量引用的形参进行修改,从而不会通过编译!

  1. #include <iostream>
  2. using namespace std;
  3.  
  4. class Test
  5. {
  6. public:
  7. void f(const int& arg);
  8. private:
  9. int value;
  10. };
  11.  
  12. void Test::f(const int& arg) {
  13. arg=10; //试图修改arg的值,此行将引起编译器错误 //error C2166: l-value specifies const object
  14. cout<<"arg="<<arg<<endl;
  15. value=20;
  16. }
  17.  
  18. void main()
  19. {
  20. int i=7;
  21. Test test;
  22. test.f(i);
  23. cout<<"i="<<i<<endl;
  24. }

  6.3 常量引用返回值

  如果你想从常量方法(函数)中通过引用返回this对象的一个成员, 你应该使用常量引用来返回它,即const X&
  也就是说你想通过引用返回的东西如果从逻辑上来讲是this对象的一部分(与它是否在物理上嵌入在this对象中无关),那么常量方法需要通过常量引用或者通过值来返回,而不能通过非常量引用返回

  1. class Person {
  2. public:
  3. const string& name_good() const; // Right: the caller can't change the name
  4. string& name_evil() const; // Wrong: the caller can change the name
  5. .
  6. };
  7.  
  8. void myCode(const Person& p) // You're promising not to change the Person object
  9. {
  10. p.name_evil() = "Igor"; // but you changed it anyway!!
  11. }

  

c++常量详解的更多相关文章

  1. Struts2常量详解

    -----------------siwuxie095 Struts2 常量详解 Struts2 的常量大多在默认的配置文件中已经配置好,但根据 用户需求和开发要求的不同,可能需要修改这些常量值,修改 ...

  2. “全栈2019”Java第六十二章:接口与常量详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  3. PHP中的变量与常量详解

    几乎所有的编程语言都会涉及到变量和常量这两个概念,PHP也不例外.本节将介绍PHP语言中的变量和常量的应用方法. 一.什么是变量和常量 在程序执行的过程中,变量存储的值可以随时改变,而常量存储的值是不 ...

  4. PHP常量详解:define和const的区别

    常量是一个简单值的标识符(名字).如同其名称所暗示的,在脚本执行期间该值不能改变(除了所谓的魔术常量,它们其实不是常量).常量默认为大小写敏感.通常常量标识符总是大写的. 可以用 define() 函 ...

  5. PHP中的 变量 与 常量 详解

    几乎所有的编程语言都会涉及到变量和常量这两个概念,PHP也不例外.本节将介绍PHP语言中的变量和常量的应用方法. 一.什么是变量和常量 在程序执行的过程中,变量存储的值可以随时改变,而常量存储的值是不 ...

  6. jsp里面自定义标签常量详解

    标签中静态常量: EVAL_BODY_INCLUDE:告诉服务器正文的内容,并把这些内容送入输出流 SKIP_BODY:告诉服务器不要处理正文内容 EVAL_PAGE:让服务器继续执行页面 SKIP_ ...

  7. JVM详解之:运行时常量池

    目录 简介 class文件中的常量池 运行时常量池 静态常量详解 String常量 数字常量 符号引用详解 String Pool字符串常量池 总结 简介 JVM在运行的时候会对class文件进行加载 ...

  8. PHP常量、变量作用域详解(一)

    PHP 中的每个变量都有一个针对它的作用域,它是指可以在其中访问变量(从而访问它的值)的一个领域.对于初学者来说,变量的作用域是它们所驻留的页面.因此, 如果你定义了 $var,页面余下部分就可以访问 ...

  9. java中的String类常量池详解

    test1: package StringTest; public class test1 { /** * @param args */ public static void main(String[ ...

随机推荐

  1. VirtualBox虚拟机上安装windows7系统

    1.下载Windows7的镜像文件 http://www.xitongcheng.com/jiaocheng/win7_article_24156.html 2.在虚拟机上安装Windows7 htt ...

  2. 转载-找圆算法((HoughCircles)总结与优化-霍夫变换

    原文链接: http://www.opencv.org.cn/forum.php?mod=viewthread&tid=34096   找圆算法((HoughCircles)总结与优化 Ope ...

  3. TW实习日记:第九天

    这两天有点忙,要改前端网页和加需求上去.所以昨天说的Vue缓存机制也没看,所以打算现在列个挖了的坑的清单: Vue缓存机制.生命周期和钩子函数 使用项目组自用组件来重写静态页面 SSM框架搭建.整合流 ...

  4. nodejs express 加载html模板

    在nodejs中如使用express框架,她默认的是ejs和jade渲染模板.由于我在使用的时候觉得她的代码书写方式很不爽还是想用html的形式去书写,于是我找了使用了html模板. 直接上代码,主要 ...

  5. c# ms chart 控件使用方法

    第一个简单的chart: spline// Create new data series and set it's visualattributes       Series series = new ...

  6. 阿里IPO法律咨询费达1580万美元 为Facebook六倍

    据路透社报道,阿里巴巴集团周五在 IPO (首次公开招股)更新文件中披露,将向美国盛信律师事务所(Simpson Thacher)以及其他为 IPO 提供咨询服务的律师事务所支付 1580 万美元的法 ...

  7. Amazon Seller Central is Temporarily Unavailable

    Seller Central is Temporarily Unavailable We apologize for the inconvenience. Our technical staff is ...

  8. python实现中文验证码识别方法(亲测通过)

    验证码截图如下: # coding:utf-8from PIL import Image,ImageEnhanceimport pytesseract#上面都是导包,只需要下面这一行就能实现图片文字识 ...

  9. PHP中的变量名,函数名,类名是区分大小写的吗

    在PHP中,自定义的函数名,类名,以及内置的函数,关键字是不区分大小写的,比如: class,Class,CLASS,while,While,ECHO,echo,NULL,Null 都是一样的. 但是 ...

  10. Scrum立会报告+燃尽图(十月三十日总第二十一次)

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2290 项目地址:https://git.coding.net/zhang ...