c++常量详解
概念
常量是存放固定且不可变值的,一旦确定初始值则在程序其它地方不可改变, 所以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. 常量函数可以被任何对象调用,而非常量函数则只能被非常量对象调用,不能被常量对象调用,如:
- class Fred {
- public:
- void inspect() const; // This member promises NOT to change *this
- void mutate(); // This member function might change *this
- };
- void userCode(Fred& changeable, const Fred& unchangeable)
- {
- changeable.inspect(); // OK: doesn't change a changeable object
- changeable.mutate(); // OK: changes a changeable object
- unchangeable.inspect(); // OK: doesn't change an unchangeable object
- unchangeable.mutate(); // ERROR: attempt to change unchangeable object
- }
7. 在类中允许存在同名的常量函数和非常量函数,编译器根据调用该函数的对象选择合适的函数
当非常量对象调用该函数时,先调用非常量函数;
当常量对象调用该函数时,只能调用常量函数;
如果在类中只有常量函数而没有与其同名的非常量函数,则非常量与常量对象都可调用该常量函数;如:
- #include <iostream>
- using namespace std;
- struct A
- {
- void f() const { cout<<"const function f is called"<<endl; }
- void f() { cout<<"non-const function f is called"<<endl; }
- void g() { cout<<"non-const function g is called"<<endl; }
- };
- void main()
- {
- A a;
- const A& ref_a = a;
- a.f(); //calls void f(),输出:non-const function f is called
ref_a.f();//calls void f () const, 输出:const function f is called
- 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所引用的对象的状态。此外,通过声明参数常量,函数的使用者可以确保他们的对象 不会被改变,也不必担心在调用函数时带来副作用。以下代码试图对声明为常量引用的形参进行修改,从而不会通过编译!
- #include <iostream>
- using namespace std;
- class Test
- {
- public:
- void f(const int& arg);
- private:
- int value;
- };
- void Test::f(const int& arg) {
- arg=10; //试图修改arg的值,此行将引起编译器错误 //error C2166: l-value specifies const object
- cout<<"arg="<<arg<<endl;
- value=20;
- }
- void main()
- {
- int i=7;
- Test test;
- test.f(i);
- cout<<"i="<<i<<endl;
- }
6.3 常量引用返回值
如果你想从常量方法(函数)中通过引用返回this对象的一个成员, 你应该使用常量引用来返回它,即const X&
也就是说你想通过引用返回的东西如果从逻辑上来讲是this对象的一部分(与它是否在物理上嵌入在this对象中无关),那么常量方法需要通过常量引用或者通过值来返回,而不能通过非常量引用返回
- class Person {
- public:
- const string& name_good() const; // Right: the caller can't change the name
- string& name_evil() const; // Wrong: the caller can change the name
- .
- };
- void myCode(const Person& p) // You're promising not to change the Person object
- {
- p.name_evil() = "Igor"; // but you changed it anyway!!
- }
c++常量详解的更多相关文章
- Struts2常量详解
-----------------siwuxie095 Struts2 常量详解 Struts2 的常量大多在默认的配置文件中已经配置好,但根据 用户需求和开发要求的不同,可能需要修改这些常量值,修改 ...
- “全栈2019”Java第六十二章:接口与常量详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- PHP中的变量与常量详解
几乎所有的编程语言都会涉及到变量和常量这两个概念,PHP也不例外.本节将介绍PHP语言中的变量和常量的应用方法. 一.什么是变量和常量 在程序执行的过程中,变量存储的值可以随时改变,而常量存储的值是不 ...
- PHP常量详解:define和const的区别
常量是一个简单值的标识符(名字).如同其名称所暗示的,在脚本执行期间该值不能改变(除了所谓的魔术常量,它们其实不是常量).常量默认为大小写敏感.通常常量标识符总是大写的. 可以用 define() 函 ...
- PHP中的 变量 与 常量 详解
几乎所有的编程语言都会涉及到变量和常量这两个概念,PHP也不例外.本节将介绍PHP语言中的变量和常量的应用方法. 一.什么是变量和常量 在程序执行的过程中,变量存储的值可以随时改变,而常量存储的值是不 ...
- jsp里面自定义标签常量详解
标签中静态常量: EVAL_BODY_INCLUDE:告诉服务器正文的内容,并把这些内容送入输出流 SKIP_BODY:告诉服务器不要处理正文内容 EVAL_PAGE:让服务器继续执行页面 SKIP_ ...
- JVM详解之:运行时常量池
目录 简介 class文件中的常量池 运行时常量池 静态常量详解 String常量 数字常量 符号引用详解 String Pool字符串常量池 总结 简介 JVM在运行的时候会对class文件进行加载 ...
- PHP常量、变量作用域详解(一)
PHP 中的每个变量都有一个针对它的作用域,它是指可以在其中访问变量(从而访问它的值)的一个领域.对于初学者来说,变量的作用域是它们所驻留的页面.因此, 如果你定义了 $var,页面余下部分就可以访问 ...
- java中的String类常量池详解
test1: package StringTest; public class test1 { /** * @param args */ public static void main(String[ ...
随机推荐
- VirtualBox虚拟机上安装windows7系统
1.下载Windows7的镜像文件 http://www.xitongcheng.com/jiaocheng/win7_article_24156.html 2.在虚拟机上安装Windows7 htt ...
- 转载-找圆算法((HoughCircles)总结与优化-霍夫变换
原文链接: http://www.opencv.org.cn/forum.php?mod=viewthread&tid=34096 找圆算法((HoughCircles)总结与优化 Ope ...
- TW实习日记:第九天
这两天有点忙,要改前端网页和加需求上去.所以昨天说的Vue缓存机制也没看,所以打算现在列个挖了的坑的清单: Vue缓存机制.生命周期和钩子函数 使用项目组自用组件来重写静态页面 SSM框架搭建.整合流 ...
- nodejs express 加载html模板
在nodejs中如使用express框架,她默认的是ejs和jade渲染模板.由于我在使用的时候觉得她的代码书写方式很不爽还是想用html的形式去书写,于是我找了使用了html模板. 直接上代码,主要 ...
- c# ms chart 控件使用方法
第一个简单的chart: spline// Create new data series and set it's visualattributes Series series = new ...
- 阿里IPO法律咨询费达1580万美元 为Facebook六倍
据路透社报道,阿里巴巴集团周五在 IPO (首次公开招股)更新文件中披露,将向美国盛信律师事务所(Simpson Thacher)以及其他为 IPO 提供咨询服务的律师事务所支付 1580 万美元的法 ...
- Amazon Seller Central is Temporarily Unavailable
Seller Central is Temporarily Unavailable We apologize for the inconvenience. Our technical staff is ...
- python实现中文验证码识别方法(亲测通过)
验证码截图如下: # coding:utf-8from PIL import Image,ImageEnhanceimport pytesseract#上面都是导包,只需要下面这一行就能实现图片文字识 ...
- PHP中的变量名,函数名,类名是区分大小写的吗
在PHP中,自定义的函数名,类名,以及内置的函数,关键字是不区分大小写的,比如: class,Class,CLASS,while,While,ECHO,echo,NULL,Null 都是一样的. 但是 ...
- Scrum立会报告+燃尽图(十月三十日总第二十一次)
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2290 项目地址:https://git.coding.net/zhang ...