C++总结:C++中的const和constexpr

2023-11-16

C++总结:C++中的const和constexpr

2014-02-18 15:31 by 付哲, 10196 阅读, 0 评论, 收藏编辑

C++中的const可用于修饰变量、函数,且在不同的地方有着不同的含义,现总结如下。

const的语义

C++中的const的目的是通过编译器来保证对象的常量性,强制编译器将所有可能违背const对象的常量性的操作都视为error。

对象的常量性可以分为两种:物理常量性(即每个bit都不可改变)和逻辑常量性(即对象的表现保持不变)。C++中采用的是物理常量性,例如下面的例子:

1
2
3
4
5
6
7
struct  A {
     int  *ptr;
};
int  k = 5, r = 6;
const  A a = {&k};
a.ptr = &r; // !error
*a.ptr = 7; // no error

a是const对象,则对a的任何成员进行赋值都会被视为error,但如果不改动ptr,而是改动ptr指向的对象,编译器就不会报错。这实际上违背了逻辑常量性,因为A的表现已经改变了!

逻辑常量性的另一个特点是,const对象中可以有某些用户不可见的域,改变它们不会违背逻辑常量性。Effective C++中的例子是:

1
2
3
4
5
6
7
8
9
class  CTextBlock {
public :
     ...
     std:: size_t  length() const ;
private :
     char  *pText;
     std:: size_t  textLength;            // last calculated length of textblock
     bool  lengthIsValid;                // whether length is currently valid
};

CTextBlock对象每次调用length方法后,都会将当前的长度缓存到textLength成员中,而lengthIsValid对象则表示缓存的有效性。这个场景中textLength和lengthIsValid如果改变了,其实是不违背CTextBlock对象的逻辑常量性的,但因为改变了对象中的某些bit,就会被编译器阻止。C++中为了解决此问题,增加了mutable关键字。

本部分总结:C++中const的语义是保证物理常量性,但通过mutable关键字可以支持一部分的逻辑常量性。

const修饰变量

如上节所述,用const修饰变量的语义是要求编译器去阻止所有对该变量的赋值行为。因此,必须在const变量初始化时就提供给它初值:

1
2
3
const  int  i;
i = 5; // !error
const  int  j = 10; // ok

这个初值可以是编译时即确定的值,也可以是运行期才确定的值。如果给整数类型的const变量一个编译时初值,那么可以用这个变量作为声明数组时的长度:

1
2
3
4
const  int  COMPILE_CONST = 10;
const  int  RunTimeConst = cin.get();
int  a1[COMPLIE_CONST]; // ok in C++ and error in C
int  a2[RunTimeConst]; // !error in C++

因为C++编译器可以将数组长度中出现的编译时常量直接替换为其字面值,相当于自动的宏替换。(gcc验证发现,只有数组长度那里直接做了替换,而其它用COMPILE_CONST赋值的地方并没有进行替换。)

文件域的const变量默认是文件内可见的,如果需要在b.cpp中使用a.cpp中的const变量M,需要在M的初始化处增加extern:

1
2
3
4
5
//a.cpp
extern  const  int  M = 20;
 
//b.cpp
extern  const  int  M;

一般认为将变量的定义放在.h文件中会导致所有include该.h文件的.cpp文件都有此变量的定义,在链接时会造成冲突。但将const变量的定义放在.h文件中是可以的,编译器会将这个变量放入每个.cpp文件的匿名namespace中,因而属于是不同变量,不会造成链接冲突。(注意:但如果头文件中的const量的初始值依赖于某个函数,而每次调用此函数的返回值不固定的话,会导致不同的编译单元中看到的该const量的值不相等。猜测:此时将该const量作为某个类的static成员可能会解决此问题。)

const修饰指针与引用

const修饰引用时,其意义与修饰变量相同。但const在修饰指针时,规则就有些复杂了。

简单的说,可以将指针变量的类型按变量名左边最近的‘*’分成两部分,右边的部分表示指针变量自己的性质,而左边的部分则表示它指向元素的性质:

1
2
3
4
5
6
7
8
const  int  *p1; // p1 is a non-const pointer and points to a const int
int  * const  p2; // p2 is a const pointer and points to a non-const int
const  int  * const  p3; // p3 is a const pointer and points to a const it
const  int  *pa1[10]; // pa1 is an array and contains 10 non-const pointer point to a const int
int  * const  pa2[10]; // pa2 is an array and contains 10 const pointer point to a non-const int
const  int  (* p4)[10]; // p4 is a non-const pointer and points to an array contains 10 const int
const  int  (*pf)(); // pf is a non-const pointer and points to a function which has no arguments and returns a const int
...

const指针的解读规则差不多就是这些了……

指针自身为const表示不可对该指针进行赋值,而指向物为const则表示不可对其指向进行赋值。因此可以将引用看成是一个自身为const的指针,而const引用则是const Type * const指针。

指向为const的指针是不可以赋值给指向为非const的指针,const引用也不可以赋值给非const引用,但反过来就没有问题了,这也是为了保证const语义不被破坏。

可以用const_cast来去掉某个指针或引用的const性质,或者用static_cast来为某个非const指针或引用加上const性质:

1
2
3
4
int  i;
const  int  *cp = &i;
int  *p = const_cast < int  *>(cp);
const  int  *cp2 = static_cast < const  int  *>(p); // here the static_cast is optional

C++类中的this指针就是一个自身为const的指针,而类的const方法中的this指针则是自身和指向都为const的指针。

类中的const成员变量

类中的const成员变量可分为两种:非static常量和static常量。

非static常量:

类中的非static常量必须在构造函数的初始化列表中进行初始化,因为类中的非static成员是在进入构造函数的函数体之前就要构造完成的,而const常量在构造时就必须初始化,构造后的赋值会被编译器阻止。

1
2
3
4
5
6
7
8
class  B {
public :
     B(): name( "aaa" ) {
         name = "bbb" ; // !error
     }
private :
     const  std::string name;
};

static常量:

static常量是在类中直接声明的,但要在类外进行唯一的定义和初始值,常用的方法是在对应的.cpp中包含类的static常量的定义:

1
2
3
4
5
6
7
8
// a.h
class  A {
     ...
     static  const  std::string name;
};
 
// a.cpp
const  std::string A::name( "aaa" );

一个特例是,如果static常量的类型是内置的整数类型,如char、int、size_t等,那么可以在类中直接给出初始值,且不需要在类外再进行定义了。编译器会将这种static常量直接替换为相应的初始值,相当于宏替换。但如果在代码中我们像正常变量那样使用这个static常量,如取它的地址,而不是像宏一样只使用它的值,那么我们还是需要在类外给它提供一个定义,但不需要初始值了(因为在声明处已经有了)。

1
2
3
4
5
6
7
8
// a.h
class  A {
     ...
     static  const  int  SIZE = 50;
};
 
// a.cpp
const  int  A::SIZE = 50; // if use SIZE as a variable, not a macro

const修饰函数

C++中可以用const去修饰一个类的非static成员函数,其语义是保证该函数所对应的对象本身的const性。在const成员函数中,所有可能违背this指针const性(const成员函数中的this指针是一个双const指针)的操作都会被阻止,如对其它成员变量的赋值以及调用它们的非const方法、调用对象本身的非const方法。但对一个声明为mutable的成员变量所做的任何操作都不会被阻止。这里保证了一定的逻辑常量性。

另外,const修饰函数时还会参与到函数的重载中,即通过const对象、const指针或引用调用方法时,优先调用const方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class  A {
public :
     int  &operator[]( int  i) {
         ++cachedReadCount;
         return  data[i];
     }
     const  int  &operator[]( int  i) const  {
         ++size; // !error
         --size; // !error
         ++cachedReadCount; // ok
         return  data[i];
     }
private :
     int  size;
     mutable  cachedReadCount;
     std::vector< int > data;
};
 
A &a = ...;
const  A &ca = ...;
int  i = a[0]; // call operator[]
int  j = ca[0]; // call const operator[]
a[0] = 2; // ok
ca[0] = 2; // !error

这个例子中,如果两个版本的operator[]有着基本相同的代码,可以考虑在其中一个函数中去调用另一个函数来实现代码的重用(参考Effective C++)。这里我们只能用非const版本去调用const版本。

1
2
3
int  &A::operator[]( int  i) {
     return  const_cast < int  &>( static_cast < const  A &>(* this ).operator[](i));
}

其中为了避免调用自身导致死循环,首先要将*this转型为const A &,可以使用static_cast来完成。而在获取到const operator[]的返回值后,还要手动去掉它的const,可以使用const_cast来完成。一般来说const_cast是不推荐使用的,但这里我们明确知道我们处理的对象其实是非const的,那么这里使用const_cast就是安全的。

constexpr

constexpr是C++11中新增的关键字,其语义是“常量表达式”,也就是在编译期可求值的表达式。最基础的常量表达式就是字面值或全局变量/函数的地址或sizeof等关键字返回的结果,而其它常量表达式都是由基础表达式通过各种确定的运算得到的。constexpr值可用于enum、switch、数组长度等场合。

constexpr所修饰的变量一定是编译期可求值的,所修饰的函数在其所有参数都是constexpr时,一定会返回constexpr。

1
2
3
4
5
6
7
constexpr  int  Inc( int  i) {
     return  i + 1;
}
 
constexpr  int  a = Inc(1); // ok
constexpr  int  b = Inc(cin.get()); // !error
constexpr  int  c = a * 2 + 1; // ok

constexpr还能用于修饰类的构造函数,即保证如果提供给该构造函数的参数都是constexpr,那么产生的对象中的所有成员都会是constexpr,该对象也就是constexpr对象了,可用于各种只能使用constexpr的场合。注意,constexpr构造函数必须有一个空的函数体,即所有成员变量的初始化都放到初始化列表中。

1
2
3
4
5
6
7
struct  A {
     constexpr  A( int  xx, int  yy): x(xx), y(yy) {}
     int  x, y;
};
 
constexpr  A a(1, 2);
enum  {SIZE_X = a.x, SIZE_Y = a.y};

constexpr的好处:

  1. 是一种很强的约束,更好地保证程序的正确语义不被破坏。
  2. 编译器可以在编译期对constexpr的代码进行非常大的优化,比如将用到的constexpr表达式都直接替换成最终结果等。
  3. 相比宏来说,没有额外的开销,但更安全可靠。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++总结:C++中的const和constexpr 的相关文章

  • Visual Studios 2015 中的“恢复 NuGet 包”没有执行任何操作

    我将解决方案从 SVN 拉入 Visual Studios 2015 代码中的一些 使用 引用出现错误 因此我尝试在右键单击 解决方案 中的解决方案时运行 恢复 NuGet 包 选项探索者 这没有任何作用 我必须手动进入 nuget 管理器
  • Qt/c++ 随机字符串生成[重复]

    这个问题在这里已经有答案了 我正在创建一个应用程序 需要生成多个随机字符串 几乎就像一个由一定长度的 ASCII 字符组成的唯一 ID 这些字符混合有大写 小写 数字字符 有没有 Qt 库可以实现这一点 如果没有 在纯 C 中生成多个随机字
  • C# 中类似图的实现

    所以我有一个对象 我们称之为 Head 它有一个对象列表 C C1 C2 C3 T T1 T2 和 M M1 M2 并且所有这些都是相互关联的 例如 Head gt C1 C2 C3 T1 T2 M1 M2 T1 gt C1 C2 T2 g
  • 如何使用c#从数据桶中获取所有文档?

    如何获取数据桶中的所有文档 我尝试过一个示例 但我只能获得一个特定的文档 这是我的代码 CouchbaseClient oclient oclient new CouchbaseClient vwspace data bucket name
  • 选择initializer_list迭代器定义

    Why std initializer list
  • 如何使用 CUDA/Thrust 对两个数组/向量根据其中一个数组中的值进行排序

    这是一个关于编程的概念问题 总而言之 我有两个数组 向量 我需要对一个数组 向量进行排序 并将更改传播到另一个数组 向量中 这样 如果我对 arrayOne 进行排序 则对于排序中的每个交换 arrayTwo 也会发生同样的情况 现在 我知
  • 如何在 Visual Basic DLL 和 C++ DLL 之间创建隔离/免注册 COM?

    我必须在 C DLL 中使用 VB COM DLL 我弄清楚了如何从 C DLL 访问 VB COM DLL 并且它可以工作 现在我遇到了一个问题 我必须使用隔离的 COM 免注册 COM 因为我无法在必须使用它的每台 PC 上注册 DLL
  • 如何调试.NET Windows Service OnStart方法?

    我用 NET 编写的代码仅在作为 Windows 服务安装时才会失败 该故障甚至不允许服务启动 我不知道如何进入 OnStart 方法 如何 调试 Windows 服务应用程序 http msdn microsoft com en us l
  • 如何在 C++ 和 QML 应用程序中使用 qrc?

    我在 Windows7 上用 c qnd Qt Creator QML 编写了 Qt Quick Desktop 应用程序 现在 我必须部署它 并且我需要隐藏 qml 文件和图像 意味着 将它们放入资源等中 我读到有一个很好的方法可以使用
  • 模板与非模板类,跨编译器的不同行为

    我在一些应用程序中使用编译时计数器 它确实很有用 昨天我想用 gcc 编译一个程序 我之前使用的是 msvc 并且计数器的行为在模板类中发生了变化 它在模板类中不再工作 过于简化的代码 Maximum value the counter c
  • 使用 OleDbCommand / OleDbDataAdapter 读取 CSV 文件

    我不明白为什么 但是当我使用 OleDbDataAdapter 或 OleDbCommand 读取 CSV 文件时 在这两种情况下 生成的数据结构良好 它识别文件头中的列 但行数据都是空字符串 我之前已经成功进行过多次 CSV 处理 因此我
  • 冒号在c中起什么作用?

    我在课堂上得到了这个例子 但我不确定它的作用 我知道冒号添加了一个位字段 但我仍然不确定这个问题 a b gt 0 3 1 运算符称为条件运算符 If b值为 gt 0 价值3被分配给a否则值1被分配给a 以 Kernighan Ritch
  • 从 C# 调用时无法识别 Powershell 命令

    这是这个的延续Question https stackoverflow com questions 66280000 powershell object returns null 66280138 noredirect 1 comment1
  • 如何禁用基于 ValidationRule 类的按钮?

    如何禁用基于 ValidationRule 类的 WPF 按钮 下面的代码可以很好地突出显示 TextBox
  • 在 C# .NET 中对非 ASCII 字符进行编码

    我想向我的应用程序发送的电子邮件添加自定义标头 标头名称只能包含 ASCII 字符 但对于值和用户可能会输入 UTF 8 字符 我必须对它们进行 Base64 编码 此外 我还必须将它们解码回 UTF 8 以便在 UI 中向用户显示它们 最
  • 应在堆栈上分配的最大数量

    我一直在寻找堆栈溢出有关应在堆栈上分配的最大内存量的指南 我看到了堆栈与堆分配的最佳实践 但没有关于应该在堆栈上分配多少以及应该在堆上分配多少的指南 有什么想法 数字可以作为指导吗 什么时候应该在堆栈上分配 什么时候应该在堆上分配 多少才算
  • 意外的 const 引用行为

    include
  • 实体框架读取列但阻止其更新

    给定一个数据库表 其中有一列包含历史数据但不再填充 实体框架中是否有一种方法可以读取该列 但在使用相同的模型对象时防止它被更新 例如我有一个对象 public class MyObject public string CurrentData
  • 检查一个数是否是完全平方数?

    我认为以下代码存在精度问题 bool isPerfectSquare long long n long long squareRootN long long sqrt n 0 5 return squareRootN squareRootN
  • 强制函数调用的顺序?

    假设我有一个抽象基类 并且我想要一个必须由派生类实现的纯虚方法 但我想确保派生方法以特定顺序调用函数 我可以做什么来强制执行它 I E base class virtual void doABC 0 virtual void A 0 vir

随机推荐

  • uniapp开发微信小程序与H5的区别(Vue)

    1 微信小程序中父组件监听子组件的事件 子组件的 listeners拿不到事件 2 微信小程序中event stopPropagation 没用 只能用vue指令的 stop来阻止 3 vue开发网页的时候 一般对于不需要响应式的数据我们可
  • python flask框架下登录注册界面_Python-用户登录 Flask-Login

    用户登录功能是 Web 系统一个基本功能 是为用户提供更好服务的基础 在 Flask 框架中怎么做用户登录功能呢 今天学习一下 Flask 的用户登录组件 Flask Login Python 之所以如此强大和流行 除了本身易于学习和功能丰
  • Nginx基础知识(个人总结)

    声明 1 本文为我的个人复习总结 并非那种从零基础开始普及知识 内容详细全面 言辞官方的文章 2 由于是个人总结 所以用最精简的话语来写文章 3 若有错误不当之处 请指出 Nginx C语言编写 战斗民族俄罗斯人所创 是高性能的 HTTP
  • Java学习之笔试面试题总结

    1 浅复制 浅克隆 被复制对象的所有变量都含有与原来的对象相同的值 而所有的对其他对象的引用仍然指向原来的对象 换言之 浅复制仅仅复制所考虑的对象 而不复制它所引用的对象 深复制 深克隆 被复制对象的所有变量都含有与原来的对象相同的值 除去
  • ctfshow-web3

    0x00 前言 CTF 加解密合集 CTF Web合集 0x01 题目 0x02 Write Up 这个题目一看就知道是一个文件包含漏洞 php input可以访问请求的原始数据的只读流 将post请求的数据当作php代码执行 GET ht
  • 【Dexie.js 踩坑】Failed to execute ‘transaction‘ on ‘IDBDatabase‘

    查了很多资料 显示都是 indexedDB 的报错 说是异步操作无法保证下一次操作时上一步已经完成 试了很多按顺序执行的方法都无效 再后来试着解决控制台显示的警告提示我版本控制有问题 我就把版本升级了 问题迎刃而解 Dexie 官方文档是英
  • LayuiAdmin模板(0积分免费下载,非单独框架)

    点击这里直接下LayuiAdmin模板 请选择 普通下载
  • 4.通过Opencv采集摄像头视频数据

    VideoCapture 虚拟采集器 一般设备号从0开始 cap read 读取视频帧 返回值有两个 第一个为状态值 读到帧为true 第二个值为视频帧 cap release 释放资源 示例程序 import cv2 引入CV库 创建窗口
  • 深度干货:制造进销存国内现状如何?2023年五大制造进销存最新盘点!

    制造进销存是什么 制造进销存的发展如何 制造进销存的优势在哪里 制造进销存都能为企业提供什么 本文将带大家深入浅出的聊聊制造进销存 全面剖析制造进销存的前世今生 并且为大家提供2023年十大制造进销存系统大盘点 一 什么是制造进销存 制造进
  • W801

    目录 文档来源 芯片概述及MCU 特性可参考 安全特性 Wi Fi 特性 蓝牙特性 低功耗模式 芯片结构 与前述介绍的外设相对应 总线结构 说明 1 AHB 1 总线 主设备列表 编辑 从设备列表 2 AHB 2总线 时钟结构 CDK中对运
  • linux入门系列18--Web服务之Apache服务2

    接上一篇文章 在了解Apache基本配置以及SELinux相关知识后 继续演示Apache提供的虚拟主机功能以及访问控制方式 如果还没看上一篇的建议先查看后再来 上篇文章 linux入门系列18 web服务之apache服务1 三 Apac
  • openwrt调试用到的

    PC间文件共享 python3 m http server 在共享的电脑上 打开浏览器 直接输入 serverip 8000 NFS Ubuntu PC端 sudo apt get install nfs kernel server sud
  • shell的模拟实现

    目录 整体框架分析 代码演示 代码分析 整体框架分析 考虑下面这个与shell典型的互动 xzy ecs 333953 date16 ls makefile mycmd mycmd cpp myexec myexec c test py x
  • Python爬虫,私活接单记录,假日到手5500,美滋滋

    前言 每年的元旦节前后 都会是Python兼职接单的小高潮 这段时间各个行业对爬虫类的需求会暴增 圈子里很多朋友在元旦假期都没闲着 两天赚上万的不在少数 所以近来问我技术变现 兼职接单问题的朋友也特别多 我把问题总结下来 发现大部分人都有着
  • dataframe中如何筛选包含特定字符串(单个字符串、字符串列表)的列?

    里斯斯里 dataframe中如何筛选包含特定字符串 单个字符python基础教程 串 字符串列表 的列 问题描述 dataframe的某一列均为字符串格式 想筛选出含有特定字符串的行 具体实现代码如下 df df 地址 str conta
  • React循环

    import React Component from react class App extends Component constructor props super props this state str 这是react数据 num
  • golang之json注释处理

    场景 json 作为现代比较常用的文本格式 本身是不支持注释的 因为它的设计初衷是作为一种轻量级数据交换格式 只需要包含数据本身 而不应该包含注释或者其他无关的信息 但是有时json内字段较多 想写一些注释说明 这些都是编程工具或者编辑器特
  • Qt Assistant使用心得

    Qt Assistant也就是Qt助手 1 如何添加文档 编辑 gt 首选项 gt 文档 gt 添加后选择需要添加的 qch文件 qch是Qt帮助文档的格式 通常Qt的帮助文档在qt目录下ducuments文件夹里 比如我的在 opt V
  • 什么是误报?如何识别误报和漏报

    不管开发人员技能多么精通 误报和漏报总是会发生 很可能是他们的代码有某种无意的错误或漏洞 为了确保尽早发现这些编码错误和漏洞 开发人员通常使用代码静态分析工具 工具会根据开发人员设置的规则检查代码 然而 代码静态分析工具并不完美 工具有时也
  • C++总结:C++中的const和constexpr

    C 总结 C 中的const和constexpr 2014 02 18 15 31 by 付哲 10196 阅读 0 评论 收藏 编辑 C 中的const可用于修饰变量 函数 且在不同的地方有着不同的含义 现总结如下 const的语义 C