const

const是C++提供的一个强大的关键字,const的用法非常多,但总的来说,const的作用只有一个:保证被修饰的内容不会被程序所修改。

const基本用法

对一个类型的对象使用const修饰即限定这个对象是只读的,不能进行修改操作,由于无法进行修改操作,这也就要求我们在声明const对象时必须同时赋值或初始化。const对象的初始化一般是如下形式:

const TypeName Var = Expression;

示例:

const int a = 0;
int const a = 0; // 等价写法
a = 1; // 编译报错

可以注意到的是,const对象的初始化是用表达式初始化的,只是在我们的示例中使用的是常量表达式。事实上,const初始化可以是如下形式:

int getA() {
return 0;
}
const int a = 0; // 字面量0是常量表达式,在编译期就能确定,a在编译期完成初始化
const int b = getA(); // getA()在编译期编译,b在运行时初始化

一个特殊情况是,期望这个const对象存在于全局作用域,那么可以在声明时加上extern修饰,那么就可以在声明时不赋初值,但必须确保程序至少有一处声明赋初值。

// A.cpp
extern const int a; // B.cpp
extern const int a = 0;

const引用

const引用是对const对象的引用,也就是说,它只是确保不去修改引用的内容,与引用的对象是否是const对象无关。

int a = 0;
const int b = 0;
const int &c = a; // 正确,可以直接修改a的值,但不能通过c修改a
const int &d = b; // 正确,b、d均不可修改

需要注意的是,把const引用绑定到临时对象上是非法的。

int a = 0;
const int &b = a + 1; // 这里a+1生成了一个临时对象
//等价于
const int temp1 = a + 1;
const int &b = temp1; const double &c = a; // 这里通过隐式类型转换生成了临时对象
// 等价于
const double temp = a;
const double &c = temp;

虽然可以通过编译,但这是无意义的引用绑定。

const与指针

const也可以修饰指针。由于多级指针的存在,const的结合也就变得复杂起来。

int a = 0;
const int b = 0; const int *c = &a; // 合法,c是一个指向常量的指针,尽管a本身不是常量
// 等价于
int const *c = &a; const int *d = &b; // 合法,b是常量,c是指向常量的指针
int *e = &b; // 非法,b是常量,普通指针无法指向常量地址
const int *f = &b; // 合法,b是常量,f是指向常量的指针

最常见的是常量指针指向常量的指针,前者表示指针是一个常量,即指向的地址不能修改,后者表示指向的地址所存储的内容是常量。常量指针和指向常量的指针是用const修饰的一级指针的两种情况,在《C++ Primer》一书中,两者分别称为顶层const底层const

const int a = 0;
const int *b = &a; // b是指向常量的指针,底层const
int c = 0;
int * const d = &c; // d是一个常量指针,顶层const
const int * const e = &a; // e是一个指向常量的常量指针

在涉及到多级指针时,可以从右往左阅读声明表达式,确认const修饰的是哪一级。

int a = 0;
int *b = &a; // b是一个一级指针
int **c = &b; // c是一个二级指针
int **const *d = &c; // d是一个三级指针
/*
从右往左阅读表达式:
1.首先声明了一个变量d
2.下一个是*,说明d是一个指针,它指向了一个对象
3.接着是const,说明它指向的这个对象不能修改
4.接着又是一个*,说明指向的对象也是一个指针
5.然后是最后的*,说明指向的指针指向的对象仍是一个指针
6.最后是int,说明最后一级指针指向的是一个int类型的地址
在理解这个声明之后很容易就可以对下面的赋值做判断
*/
d = &c; // 正确,d是一个普通指针
*d = &b; // 错误,解引用d得到的是一个常量对象
**d = &a; // 正确,二次解引用d得到的是一个普通指针

constexpr表达式

前面提到,可以用常量表达式或者非常量表达式初始化const对象。所谓的常量表达式是指在编译期就可以得到结果的表达式,由常量表达式初始化的const对象也可以参与组成常量表达式。在某些时候,我们希望一个表达式能在编译期就得到确定,但在复杂的项目中确认一个对象是不是常量表达式非常困难,由此C++引入了constexpr关键字,用于显式说明某个对象是常量表达式。

constexpr int a = 0; // 正确,用字面量0初始化常量表达式
constexpr int b = getB(); // 正确与否取决于getB()是否是常量表达式

由于需要在编译期就确定constexpr对象的值,这也就对指针和引用的constexpr初始化提出了更严格的要求:一般情况下,定义在函数内的对象地址无法在编译期确定,因此无法作为初始化常量表达式的值,相反,全局对象可以。

const与函数

const可以修饰函数的形参。

int LiF(const int lif);
// 正确,在函数内部不能修改lif
// 当然,形参本身只是一个拷贝,在函数调用过程中发生的修改并不会反馈到实参
int LiF(const int *lif);
// 正确,保护原数据不被修改
int LiF(const int &lif);
// 正确,这是最常用的写法,兼具效率与安全性

const还可以修饰函数的返回值。const确保函数的返回值不会被修改,即无法用作左值。

const int& LiF(int &lif) {
return lif;
}

const与类

const可以修饰类的成员,由于调用构造函数时就已经确认了对象的内容,也就是说,const成员需要在构造函数之前初始化,那么,被修饰的类成员只能通过初始化列表初始化。

class LiF {
public:
LiF(int _lif): lif(_lif) {}
private:
const int lif;
};

const可以修饰类的成员函数,被修饰的成员函数称为常成员函数,常成员函数可以被所有对象调用,但常对象只能调用常成员函数。这是因为,成员函数的参数列表里隐式传递了一个this指针,用const修饰成员函数,实际上是修饰this,而const *是没办法转换成普通指针类型的,故不能调用普通成员函数。又由于函数重载不会忽略掉底层const,故根据成员函数的const也可以构成重载。非常对象会通过精确匹配找到普通成员函数,而常对象则会匹配到对应的常成员函数。

class LiF {
public:
int get() { return lif; }
int get() const { return lif; } // 常成员函数重载
private:
int lif;
}; LiF l1;
const LiF l2;
l1.get(); // 调用的是int get();
l2.get(); // 调用的是int get() const;

有时我们希望类的成员能记录某些信息,即便是在const对象内。这时就需要一个永远可变的成员,对应地,C++提供了mutable关键字。

class LiF {
public:
void count() const {
lif++;
}
private:
mutable int lif;
}; LiF l1;
const LiF l2 = l1;
l2.count();

const还可以修饰成员函数的返回值,与普通函数的const返回值类似,以禁止链式调用,或者说禁止返回值成为左值。

class LiF {
public:
const LiF& operator= (const LiF &l) {
lif = l.lif;
return *this;
}
const LiF& set(int _lif) {
lif = _lif;
return *this;
}
private:
int lif;
}; LiF l1, l2, l3;
l1 = l2 = l3; // 合法
(l1 = l2) = l3; // 非法,重载后的赋值运算符返回值是常量,不能再次赋值 LiF l4;
l4.set(1); // 合法
l4.set(1).set(2); // 非法,set(1)之后返回的是常量this

C++:const的更多相关文章

  1. 面试问题5:const 与 define 宏定义之间的区别

    问题描述:const 与 define 宏定义之间的区别 (1) 编译器处理方式不同     define宏是在预处理阶段展开:     const常量是编译运行阶段使用: (2) 类型和安全检查不同 ...

  2. 读书笔记:const和readonly、static readonly 那些事

    C#中表示不变的量(常量)的两种形式:const 和readonly const 是静态常量 readonly 是动态常量 严格的来讲:const 应该称为常量 而readonly 则应称为只读变量. ...

  3. C++的那些事:const用法面面观

    一.const是什么 在 C/C++ 语言中,const关键字是一种修饰符.所谓“修饰符”,就是在编译器进行编译的过程中,给编译器一些“要求”或“提示”,但修饰符本身,并不产生任何实际代码.就 con ...

  4. 转:const“变量”、define的常量和static 变量

    首先讲C编译器的内存分配: 代码区 数据区 用户区=线程栈+堆 其中的数据区存储:常量(define)+静态变量(static)+符号集(const)+全局变量   然后讲一下编译的大致顺序: 注释- ...

  5. 函数定义从零开始学C++之从C到C++(一):const与#define、结构体对齐、函数重载name mangling、new/delete 等

    今天一直在学习函数定义之类的问题,下午正好有机会和大家共享一下. 一.bool 类型 逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC+ ...

  6. C语言关键字 - 铁布衫:const 转载

    const 描述:相传C世界中出现了一件极品装备const,它能的出现,让天下所有的刺客,黑客都失业了,在它的保护下,所有的变量都可以完好无损. 作用:const是constant的简写,表示海枯石栏 ...

  7. C++ Prime:const的引用

    可以把引用绑定到const对象上,就像绑定到其他对象上一样,我们称之为对常量的引用.与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象: ; const int &r1 = ci; ...

  8. C++雾中风景3:const用法的小结

    const作为C与C++共有的关键字,很多使用的方式大同小异.但由于C++是一门面向对象的语言,在类和对象中有更多的使用规则.之前学习C语言的时候就被const这个关键字搅得焦头烂额,正巧也借这篇文章 ...

  9. C和C++的内存操作小贴士(一):const char*的内存释放问题

    C和C++的内存操作一直是困扰开发人员的老问题,基本概念相信老司机们都很清楚了,在这里就不做过多的描述了,只是把在实际开发中可能遇到的一些小问题的案例列举下,供大家参考.“C和C++的内存操作小贴士” ...

  10. C++笔记016:const 基础

    原创笔记,转载请注明出处! 点击[关注],关注也是一种美德~ 笔记十六关于const关键字,在C语言和C++中const的表现是不同的. 先看一下const基础知识. 对const的初级理解:cons ...

随机推荐

  1. U盘安装CentOS 7提示 “Warning: /dev/root does not exist, could not boot” 解决办法

    1.把U盘的Lable(即标签)修改成centos 2.在安装界面上按TAB键,修改启动路径,把”CENTOS\x207\x20x86_64″改成 “centos”

  2. 【spring】全局异常 globalexception 处理

    全局异常 globalexception 处理   一般在做api开发时我们希望将所有业务层抛到controller异常都集中处理一下.比如对异常差异化报警.转发不同页面.封装不同http状态码.集中 ...

  3. Vim操作:打开文件

    1.打开文件并定位到某一行 vim +20 vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php # 定位至第20行 2 ...

  4. Vue-cli3脚手架工具快速创建一个项目

    1.首先全局安装一下vue-cli3 npm install -g @vue/cli 或 yarn global add @vue/cli vue -V查看版本(这里注意V是大写哦) 2.vue cr ...

  5. JS高阶---对象创建模式(5种)

    [前言] 函数高级部分先看到这里,接下里看下面向对象高级部分 .对象创建模式 .继承模式 [主体] (1)Object构造函数模式 案例如下: 测试结果如右图所示 (2)对象字面量形式创建 案例如下: ...

  6. 201871010121-王方-《面向对象(java)程序设计对象》第十周学习总结

    王方第九周Java实验总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.co ...

  7. VC 静态库与动态库(一)介绍

    定义: 静态库与动态库都属于库,库从本质上来说就是种代码重用的方式. 把需要重复使用的公共代码抽离出来,生成库文件,外部程序只需包含库文件,调用相关接口即可 静态库与动态库区别: 静态库:需要库的.h ...

  8. LG2053/BZOJ1070 「SCOI2007」修车 费用流

    问题描述 LG2053 BZOJ1070 题解 将\(m\)个修理工拆为\(n \times m\)个,将修理工和车辆看做二分图,连出一个完全二分图. 边流量为\(1\),费用为时间,费用流即可. \ ...

  9. onchange onpropertychange 和 oninput 事件的区别

    onchange 事件在内容改变(两次内容有可能还是相等的)且失去焦点时触发. onpropertychange 事件却是实时触发,即每增加或删除一个字符就会触发,通过 js 改变也会触发该事件,但是 ...

  10. MongoVUE(1.6.9.0)登录提示:Connection was refused的解决办法

    日志文件上描述: UserNotFound Could not find user admin1@diva 2015-10-13T12:12:22.208+0800 I NETWORK [conn1] ...