专栏首页秘籍酷多才多艺的const

多才多艺的const

在C++中,关键字const可以用来修饰任何作用域内的变量、函数参数、函数本体、函数返回值、成员函数、迭代器,也可以用来修饰指针本身和指针目标,可谓多才多艺,我们要详细了解其内部细节,以及逻辑奥秘,让这把多功能瑞士军刀尽情发挥其作用,同时不要割伤自己哟!

1. 定义一个常全局变量:

const int global = 100; // 初始化之后不可再赋值

这样的global实际上是一个常量,这是C++用来取代宏定义的其中一种措施,const常量有类型检测,提高编译器的效率。

2. 定义常指针,这有两个版本,分别是:

const int *p1 = &a; // p1不能修改它所指向的目标 int const *p1 = &a; // p1的另一种等价形式 int *const p2 = &a; // p2本身不可修改

上述代码中的p1经常被用作函数参数,用以限制指针的权限,在安全性方面功不可没。p2用的很少,我们很少需要一个本身指向不可变的指针

3. 定义一个STL的常迭代器,这也有类似的两个版本:

vector<int>::const_iterator it1; // it1不能修改它所指代的目标对象 const vector<int>::iterator it2; // it2本身不可修改

由于迭代器实际上就是广义指针,因此it1实际上相当于上述代码的p1,it2相当于上述代码的p2,同理it1用的较多,用来限制迭代器的权限。

4. 定义一个类的内部常成员,包括常成员数据和常成员方法:

class text { const int aconst = 100; // 常成员数据,必须初始化 void func(void) const; // 常成员方法,只能由常对象调用 static int astatic; };

类的常成员数据aconst,意味着类对象无法对其修改,这个很容易理解。至于类的常成员方法func就破费脑力了,语法上的理解是,func不能修改任何一个类对象的bit,这是显而易见的,这也正是C++标准对const成员方法的定义。

来近距离看看func方法的使用:

int text::astatic = 100; // 类外初始化静态数据 void text::func(void) const // 类外定义const成员方法 { astatic = 200; // 没毛病,astatic不是对象数据,此处可以被修改 aconst = 200; // 错误!const型函数不可修改类对象数据 }

定义如上的成员方法很重要,因为它明确地告诉了类的使用者,哪些函数可以修改类对象信息,哪些不会修改类对象信息。

像 func() 这样的函数const成员方法,只能被常对象调用,以确保不违反权限紧缩原则,比如:

text t1; // 普通对象t1 const text t2; // const型对象t2 t1.func(); // 错误!普通对象不可调用常成员方法 t2.func(); // 没毛病

注意,函数本体的const属性是可以被视为重载的依据的,换句话说如果以上类text提供了non-const版本的func函数,那么t1将会自动调用non-const那个版本。

以上陈述似乎平淡乏味,但考虑C++语法规定的这种bit-wise特性的常成员在处理类成员指针,并且指针指向类外部内存时,情况也许会变得有趣。

5. bit-wise和logical-wise常特性

所谓bit-wise constness指的是类对象的内部内存意义上只读约束,而loigcal-wise指的是逻辑意义上的只读约束。编译器没有智能,它只能实现bit-wise意义上的约束,下面的例子讨论const成员 operator[ ] 的表现可以帮助理解:

class text { ... ... char &operator[ ](int pos) const; private: char *p; }; // const关键字保证了该成员方法只能被 const 对象调用 char &text::operator[ ](int pos) const { return p[pos]; }

上述代码中,类text存储真正字符串的地方是指针 p 指向的内存区域,假设此时定义一个 const 型的常对象,那么它只能保证类内部信息(也就是p本身)不被修改,却无法保证其所指向的内存区域的安全性。来看下面的代码:

const text ct("abcd"); ct[0] = 'A'; // 一个被编译器允许却跟逻辑相悖的语句

此处ct是一个const型对象,从字面理解出发,我们应该认为ct对象是一个常量,我们应该无法通过任何方式修改其内部信息,但可惜,ct里面的字符串信息实际上并不存在在类内存区域中,于是就出现了可以对一个const型对象进行赋值的有趣现象。

解决以上问题也不难,可以改写 operator[ ] 成员方法的返回值类型:

const char &operator[ ](int pos) const;

此时就再也不能对 ct[0] 赋值了,但这只是个个例,关键我们要掌握的内容是:如果我们的类对象拥有成员指针,那么一般意义上的const成员方法只能保证bits-wise的常量性(即保证类对象内部的内存信息不被修改),但无法保证所谓的 logical-wise的常量性(即保证逻辑上哪些信息不被修改)。

关于 const 的讨论还没有结束,他是个才艺多面手,后续有机会再继续探索

本文分享自微信公众号 - 秘籍酷(mijiku040),作者:林世霖

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-12-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 话说const修饰符与static修饰符

    这也是面试和笔试中经常会考到的问题,特别是static我面试了四家就考了两家,但是一般会放在前两题,属于特别基础的题目了。

    用户2617681
  • C语言(不明觉厉的cv限定词)

    以上对话虽然是杜撰的,但也绝非空穴来风,的的确确有类似的面试场景,我们不要笑话别人,也不能允许以后被别人笑话。比如上述对话中提到的cv限定词,现在就得立刻马上就...

    用户2617681
  • C语言(关键字const)

    const是C语言中最坑爹的关键字,典型挂羊头卖狗肉,const本意是常量,但是C语言const只能用来定义只读变量。

    用户2617681
  • C++核心准则ES.50:不要执行去掉const属性的转换

    It makes a lie out of const. If the variable is actually declared const, modifyi...

    面向对象思考
  • C++primer学习笔记(一)

    震八方紫面昆仑侠
  • const的那些事

    在我们使用c/c++的时候,或者在面试的时候,会被问道与const相关的问题,比如const修饰放在指针的哪个位置,const修饰的引用传递与值传递等等,这些究...

    公众号guangcity
  • 【ES6基础】const介绍

    在ES6之前,JavaScript被其他编程语言诟病没有定义常量的能力,甚至在大多数企业的开发文档中,对于常量的定义都使用var。一般经常会使用大写字母和下划线...

    前端达人
  • 话说const修饰符与static修饰符

    这也是面试和笔试中经常会考到的问题,特别是static我面试了四家就考了两家,但是一般会放在前两题,属于特别基础的题目了。

    用户2617681
  • 常用JS函数-数组扁平化,缓存函数,柯里化函数,防抖和节流函数

    这个数组有很多层,我们现在需要将它变成一层的应该怎么做呢?结合我们前面讲过的reduce和递归我们很容易写出这个方法:

    蒋鹏飞
  • ES5 to ESNext —  自 2015 以来 JavaScript 新增的所有新特性

    这篇文章的出发点是为了帮助前端开发者串联 ES6前后的 JavaScript 知识,并且可以快速了解 JavaScript 语言的最新进展。

    苏南

扫码关注云+社区

领取腾讯云代金券