`
caozuiba
  • 浏览: 908438 次
文章分类
社区版块
存档分类
最新评论

C++ 惯用法: const 常量和字面量

 
阅读更多

原文:C++ 惯用法: const 常量和字面量
作者:Breaker <breaker.zy_AT_gmail>


C++ 中 const 关键字、常量和字面量的惯用法

关键字:const, enum, const_cast, const_iterator, mutable, 左值, 字面量

本质和非本质上的常量

字面量 (literal),是指在 C++ 中直接书写的数字、字符和字符串,如 0x3f, '+', 1.23e-15L, "hello", L"world",具体语法参考 [CPP_LANG] 4.3.1 字符 4.4.1 整数 4.5.1 浮点数 5.2.2 字符串

左值 (l-value),是指想尽办法可以让其放到 = 左面的对象,这里的 = 是赋值而不是初始化,这里的对象可以是类类型和基本类型。想尽办法主要指强制转换和指针地址操作

本质上的常量,指非左值,字面量、enum 枚举值是非左值

字面量的 const 变量

是否能通过强制而修改,由编译优化和运行平台决定,这是 Win7 VC 2010 的情况:

通过 const_cast 修改字面量的 const 变量 字面量的 const 变量 是否能修改
static 数字字面量 const 不可修改
non-static local 数字字面量 const 可修改
non-static local const char[] 可修改
static const char[] 不可修改
const char* 字符串字面量 不可修改

这些通过强制而修改字面量 const 变量的代码很难移植,并且不是正常的逻辑,所以认为字面量 const 变量是本质上的常量是合理的

const T& 引用

参考 [CPP_LANG] 5.5 引用

和字面量的 const 变量不同,字面量的 const T& 会引用一个临时变量

类类型也是如此,并且 const T& 的初始值可以不是类型 T 的,会自动隐式转换,而这一点对于 non-const T& 不成立的,如:

非本质的常量

非本质的常量比本质的常量更容易理解和确定,通常是函数的 const 参数接受的 non-const 初始化,如:

非本质的常量的目的,是在一定执行区间内约束代码(不保证区间外是否 const),产生试图修改 const 的编译错误

常量求值

本质上的常量,可以在编译期求值,它们或化为指令立即数,或保存在目标文件 (.obj) 和可执行文件 (.exe/.dll) 的特殊位置,如 .rdata 区段

也因在编译期求值,本质上的常量可以作为非类型模板参数,只需注意指针类型模板参数只接受外部链接对象的地址,而字符串字面量是内部链接的,这时用外部链接的字符数组代替即可

非本质的常量,在运行时求值

有一些在运行时求值的非平凡情况,评估它们的常量本质性:

常量存储

左值一定分配存储,因此可以取其地址

本质的常量(非左值)不一定分配存储,是否分配由具体情况、编译优化和运行平台决定,参考 [CPP_LANG] 5.4 常量

  • enum 不分配存储

  • 字面量的 const 变量,如果不引用其地址,如创建指向它的指针或引用,则也不需要分配存储,但具体视编译优化而定

  • 类的 static 整数 const,如果不引用其地址,则可以只用声明式而不用定义式,此时不需要分配存储;如果引用其地址,则需要定义式,并且分配存储

const 对比 volatile

const 关键字的横向对比物是 volatile,对比两者可以更明白本质

  • 和编译优化相关

    volatile 是防止变量访问被激进优化的修饰字,volatile 变量的每次读写都会产生实际的内存访问指令,以防止因编译期不可知的因素(如并发线程的共享变量访问)而优化去除 volatile 变量的内存访问(如访问寄存器中的旧值)

    本质 const 可能被优化存储,如存储到 ROM 中

    优化一定和具体编译器与平台有关,这里 C++ 是定义一种适当的目的描述,而非实现细节,如 VC 2005+ 的 volatile 变量读写带有 Acquire/Release 语义以防止编译期指令 reorder,但仅在 IA64 上保证相同的运行时模型,x86 x64 上仍允许运行时 CPU reorder

    本质 const 的存储也是平台相关的,所以上面表格中的 const_cast 很难移植

  • 非本质的 const 和 volatile

    非本质的 volatile 此称谓并不适当,权作和 const 对比,通常是函数的 volatile 参数接受的 non-volatile 初始化,如 InterlockedExchange(volatile LONG*, LONG) 中,它的目的和 const 参数相似,是在一定执行区间内约束代码(不保证区间外是否 volatile),防止那里的激进优化

  • 用 const_cast 强制去除 const, volatile 修饰字

    const_cast 只能去除非本质 const 的 const 修饰

  • 指向 const/volatile 的指针和 const/volatile 指针,参考 MSDN: const and volatile Pointers

const_iterator

一些 STL 容器如 vector 有 iterator 和 const_iterator 两种迭代器

vector<T>::const_iterator 是 const T*
const vector<T>::iterator 是 T* const

const 和 non-const 方法

逻辑上的 const 方法

参考 [CPP_LANG] 10.2.7.1, 10.2.7.2 mutable

有时我们需要在 const 方法里修改对象的状态(成员变量),这时需要用 mutable 修饰那个被修改的成员变量

典型的惯用法:返回对象状态时,先检查其 cache 值,如果需要更新,则修改其值,如:

从 Widget 用户的角度看,Widget::area() 是具有 const 逻辑的

方法的 const 和 non-const 版本

一些 STL 容器如 vector 有同一个 operator 的 const 和 non-const 两个版本,如 vector<T> 实例化后有两个 operator[]:

第一个 operator[] 可用来充当左值(准确的说是可变左值 modifiable l-value),因为所有的左值都是右值,所有它也可充当右值(注意 右值和非左值的区别)

那第二个版本 const 版本的 operator[] 岂不是多余

其实它和非本质 const 配合,用来约束代码,逻辑很合理:如果 vector<T> 是 const,那么每个元素都应该是 const,反之,假如元素能修改,就称不上是 const vector<T>,如:

分清两个位置 const 的作用:

作用 1, 返回值 const T&: 产生约束保护
作用 2, const 方法 (const* this): 产生重载规则,C++ 不能仅通过返回不同类型重载函数

也可以理解为,同一方法概念的两个版本:

non-const 版: set 式方法
const 版: get 式方法

转接 non-const 方法到 const 方法

虽然区分同一方法概念的两个版本,但大多数时候,const 和 non-const 版本的内部操作相同,仅在传入参数和返回值类型上不同

为了简化重复代码,可利用 static_cast/const_cast 将 non-const 方法转接到 const 方法,如 vector<T> 的 non-const operator 可如下转接:

反向的,将 const 转接到 non-const 上会违背 const 承诺,不要那么做

参考

[CPP_LANG] 《C++ 程序设计语言》特别版, Bjarne Stroustrup

[EFF_CPP] "Effective C++", 3Ed, Scott Meyers, 条款 02, 03

分享到:
评论

相关推荐

    C/C++:const常量与define宏定义的区别

     const常量是编译运行阶段使用。  (2) 类型和安全检查不同  define宏没有类型,不做任何类型检查,仅仅是展开。  const常量有具体的类型,在编译阶段会执行类型检查。  (3) 存储方式不同  define宏仅仅...

    C++:关于const的思考.doc

    C++:关于const的思考.doc onst的思考 1、什么是const? 常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。(当然,我们可以偷梁换柱进行更新:) 2、为什么引入const?  const...

    Const 用法小结

    C++中的各种Const用法小结:const常量,const 修饰类的数据成员等等

    C语言中const定义常量

    本文章是关于C语言中const定义常量 。

    C#箴言:定义常量的两种方法

    C#箴言:定义常量的两种方法   在C#中定义常量的方式有两种,一种叫做静态常量(Compile-time constant),另一种叫做动态常量(Runtime constant)。前者用“const”来定义,后者用“readonly”来定义。  对于...

    C++基本功:全面掌握const、volatile和mutable关键字.docx

    C++基本功:全面掌握const、volatile和mutable关键字

    [C++中】const常量与define宏定义的区别

    较详细地介绍了const常量与宏定义的区别

    C++ 中const总结

    C++ 中const总结 const 限定符把一个对象转换成一个常量,如 const int bufSize=512; 申 明 bufSize 的时候,如果不将它初始化,那么编译器会报错:const object must be initialized if not extern。因为 bufSize ...

    C++函数后加const的意义 pdf

    C++函数后加Const关键字的意义。C++类中经常会看到某函数后面加Const关键字,该文档说明了这些Const关键的意义和用法。

    PHP const定义常量及global定义全局常量实例解析

    1.const 定义的常量和define()定义的常量的区别? 希望一个成员变量不被修改 例如圆周率 3.1415926 定义:const 常量名=值; 没有$符 访问:类名 ::常量名 或 接口名::常量名 &lt;?php defined('TEXT'); //检查一...

    C++中const用法全解

    const在C++中占有重要作用,属于小兵立大功的典型,本文档详细介绍了如何使用const

    C#变量和const常量的使用

    C#变量和const常量的使用

    简单的词法分析器

    2、输入类似如下的保留字const错误的常量说明串: Aconstt count=10,sum=81.5,char1=‘f’; 输出类似下面的错误提示信息: It is not a constant declaration statement! Please input a string again! 3、输入类似...

    std::string、char*、const char*转托管byte数组或托管字符串String

    std::string、char*、const char*转托管byte数组或托管字符串String

    C语言中const和C++中的const 区别详解

    C++的const和C语言的#define都可以用来定义常量,二者是有区别的,const是有数据类型的常量,而宏常量没有,编译器可以对前者进行静态类型安全检查,对后者仅是字符替换,没有类型安全检查。 而C语言中的const与...

    跟我一起学C++

    用const给字面常量起个名字(标识符),这个标识符就称为标识符常量;因为标识符常量的声明和使用形式很像变量,所以也称常变量 定义的一般形式: const 数据类型 常量名=常量值; 数据类型 const 常量名=常量值; ...

    c++ const引用与非const引用介绍

    const引用是指向const对象的引用。 代码如下: const int i = 10; const int &ref = i; 可以读取ref,但不能修改... const引用可以初始化为不同类型的对象或者右值(如字面值常量),但非const引用不可以。 代码如下: //

    C++ 常量成员常量返回值详解

    1)常量成员函数,不能修改类数据成员,不能调用非常量函数。 2)常量成员函数的作用,可以有效的将类的函数分为可以修改类的函数,和不能修改类的函数;以后应该善于使用常量成员函数。 3.返回常量的函数,可以是...

    C++ const应用总结

    C++ learning ---const用法总结,将平时不同情况的const用法,进行了详细的总结。是学习C++的好帮手

    utf-8、ANSI、Unicode相互转化c++实现

    utf-8、ANSI、Unicode相互转化c++实现 std::string ConverANSI2UTF8(const std::string & str); std::wstring ConverANSI2Unicode(const std::string str); std::wstring ConverUTF82Unicode(const std::string str)...

Global site tag (gtag.js) - Google Analytics