C++ Reference 的“三位一体”诠释
C++ 是介于汇编语言与高级语言之间的一种“全能”语言。它的能力是其他任何基于VMA(冯-诺曼架构)计算机的高级程序设计语言无法望其项背的,而性能也只有C语言可与之伯仲。
然而长期以来,喜欢C++和憎恨C++的阵营,恶斗了三十多年,谁也说服不了谁。其中的一个重要原因是:C++的逻辑和语意难以分割,计算机学术界很难把它形式化(比如具有像Haskell那样的数学美), 这和量子力学里的不确定性类似,爱因斯坦就对此讨厌之极。
这里我不想评论两方的对错,但是我想就C++的一个最基本的概念,也是最重要的概念之一的“reference",作一番论述,看看是否说得通。
读者如果读过拙文“ http://blog.csdn.net/ly8838/article/details/38638491”,可能会对reference微妙的一面有所共鸣的。这里我想进一步谈谈reference的一些细节。
要想叙述reference,我们不得不从更基本的定义 - 直接对象(direct object)说起.
C++中,直接对象是“常规变量”(normal variable), 具有“值语意”(value semantics),和rvalue 语意。 这还没有引入reference,就已经陷入逻辑上“循环定义”的怪圈了。要想跳出,只有举例:
long x;
MyClass m; // MyClass is a user defined class
上面x和m都是“直接变量”,它们在内存中的区域可以不同:可以在程序的静态空间(全程变量),可以在工作堆栈内(自动变量),也可在动态空间内(间接地包含在母object中)。由于所在区域不同,这些直接变量对程序性能的特征影响很大(以后的博克会提及)。但是最重要的,是它们和传统的“基本型变量(primitive type variable)”在语法和用法上的相似。这是C++为何会支持它的重要原因。
对于首先学习C#,JavaScript, 或其他现代语言的程序员来说,“直接变量”几乎没有对应的概念。而更加令人不解的是,在“动态空间(heap)”中,直接变量只能“间接”地存在。而能够“直接使用”动态变量"的唯一方法只有“指针”和“reference”. 这里又陷入了循环:解释直接变量时,又无法绕开“指针”和“reference”。
那么reference到底是什么呢?看看微基的解释,(http://en.wikipedia.org/wiki/Reference_(C%2B%2B)),它几乎也是同意反复,最后只有用“比喻”来解释。也就是说,不用“指针”,“内存地址”等概念就无法解释。
那么,通俗上的说法“reference 是变量的alias (同体异名?)”是否说得通呢?
我认为,对于初学者来说,alias可谓最好的“比喻”。请看:
int x = int(10); // line1
int & r = x; // line2: r is an alias of x
r++; // line3: x is now 11
r = 3; // line4: x is now 3
assert (&r == &x); // line5: r points to x
我们看到,上面的程序显示,r 和 x 几乎没任何区别,好像是形影相随,难分难舍。这正是C++的宗旨:让reference的变量和直接变量具有“等同”的语法,用法。
作为初级程序员来说,知道到这个用法也就够了,但是“一点点知识是危险的”。如果我们以为x 和 r 是一个东西,那就错了:
首先,x 是“直接变量”,而r是间接变量,所以它们在语意上是完全不一样的(编译出来的代码大相庭径)。你可以说这与我无关,会应用就行了,这也不错,不过并不能高枕无忧:在另一个环境里,你可能就会因为不清楚object reference 和 direct object 的区别而出错,或是写出性能差别极大的程序。
从语意上看,line2 几乎等于 r = &x (取x的地址),无怪乎许多人认为 reference 其实就是指针, 而且line5证明它的确指着它的“直接变量”。知道这一点,对于下列代码很重要:
class X {…}
void myXFunction(X x) {…}
void myXFunction2(const X & x) {…}
如果认为X& 是 X 的“alias”,而且用法一样, 那么你可能不需要myXFunction2,但是如果你知道X&的语意和X * 一样,你才会推论出myXFunction2的性能一般会比myXFunction好,特别是当X里面包含大量“成员”时,myXFunction2不用copy 内容,只需copy地址 (我们说这是浅拷贝- shallow copy), 而myXFunction必须传入的内容copy 到argument变量中(我们说这是深拷贝- deep copy)。
在这里,语意盖过了逻辑。const X &x 的用法,特别是语意,最好用指针来诠释才说得过去。
好吧,那就说reference是指针吧,可是它又不能解决上面程序line4的用法:
因为对一个指针赋值会使得它改变指向。r 若为指针,line4 会使得 r 指向地址为 3 的内存元,这和 alias 的概念又风马牛不相及。
幸运的是实际情况并非如此:r 没变(指向没变),倒是 x 变了,在这个用例中,对 r 的 alias 诠释从语法和语意上都双双胜过了指针诠释。
综上所述,reference 就像一个具有“波粒二象性”的光子, 它具有“pointer-alias” 二象性,不从语法-用法-语意的三方面理解,无法认识 reference 的真谛。
如果仅有二象也就罢了,OOP (C++ 支持OOP)有一个重要的概念,叫 polymorphism, 说的是一个“变量”其实可以是同时代表“基类”和其衍生类(derived class):它既可以是A也可以是B(假设A是B的基类)(又是典型的量子悖论)。而这种 polymorphic 的变量,只能是指针和 reference,不可能是“直接变量”。这一特征又给 reference 加了另一顶帽子:polymorphic variable, 通俗说的话,它是具有同时拥有不同身份的“怪物”。这里的详情下次再谈论。
为什么polymorphic variable只能是指针和reference呢?我个人认为,这无法用形式逻辑和常规逻辑来解释,只能用指针的性质和C++ 的object model内存结构来解释 - 这又是一个语意层面才能自圆其说的概念,是C++量子特性的另一佐证。以后我会用专文来诠释C++这一重大问题。
总之,一个reference 的概念,包含了三种完全不同的诠释,可以俗称为三位一体:
- Alias (同体异名)
- Pointer (指针)
- Polymorphic variable (多身份变量)(我不喜欢多状变量这一业界内常用的翻译,认为它完全是概念混淆的,错误的,这里没有“状态”,只有身份)
到此可以做个总结,本文宗旨不是搬弄词汇,而是企图从逻辑,数学上诠释C++的一些基本概念,以便抛砖引玉,唤起你对C++的深层思考。
此文的结论是:
1)C++的一些基本概念不是形式逻辑和传统数学可以定义得清楚的。只有从“语意”上(编译目标代码)和“物理”上(计算机内存的结构)才能诠释。
2)C++的语意(程序的硬件表达),是理解C++的关键。其他的语言,如Java,C#, JavaScript等,更接近数理逻辑,不用太在意其“语意”,95%的都能理解。
3)不懂C++语意,对C++程序员的深化和提高,写出高质量,高性能的C++程序,有着不容忽略的负面影响。
杨镰:2014-8-18 西雅图
C++ Reference 的“三位一体”诠释的更多相关文章
- ASP.NET Core: You must add a reference to assembly mscorlib, version=4.0.0.0
ASP.NET Core 引用外部程序包的时候,有时会出现下面的错误: The type 'Object' is defined in an assembly that is not referenc ...
- 【转】Django Model field reference学习总结
Django Model field reference学习总结(一) 本文档包含所有字段选项(field options)的内部细节和Django已经提供的field types. Field 选项 ...
- (转) Qt 出现“undefined reference to `vtable for”原因总结
由于Qt本身实现的机制所限,我们在使用Qt制作某些软件程序的时候,会遇到各种各样这样那样的问题,而且很多是很难,或者根本找不到原因的,即使解决了问题,如果有人问你为什么,你只能回答--不知道. 今天我 ...
- 重新诠释的OSGi规范
上周五部门开会讨论新一代产品(基于.net Winform)的设计规范,从设计规范慢慢讨论到体系结构等架构存在的问题,诸如菜单.工具条.状态条.界面布局等不能实现配置化和自动化,子系统之间拥有强依赖, ...
- undefined reference to `__android_log_print'
使用android studio 编写NDK代码时出现错误:undefined reference to `__android_log_print' 解决办法: eclipse andro ...
- CentOS 6.5 编译 PHP-7 报错:undefined reference to `libiconv_open 无法编译 PHP libiconv
./configure --with-mysql=/backup/mysql --with-freetype-dir --with-jpeg-dir --with-png-dir --with-zli ...
- Qt - 错误总结 - 在自定义类头文件中添加Q_OBJECT 编译时报错(undefined reference to ‘vtable for xxThread)
错误提示:在添加的QThread子类头文件添加Q_OBJECT时,编译程序,出现"undefined reference to 'vtable for xxThread'"错误提示 ...
- Conditional project or library reference in Visual Studio
Conditional project or library reference in Visual Studio In case you were wondering why you haven’t ...
- Qt经典出错信息之undefined reference to `vtable for classname
原文链接:Qt经典出错信息之undefined reference to `vtable for classname 这个出错信息太常见了,用过Qt两个月以上的朋友基本上都能自己解决了,因为太经典了, ...
随机推荐
- 利用python分析nginx日志
最近在学习python,写了个脚本分析nginx日志,练练手.写得比较粗糙,但基本功能可以实现. 脚本功能:查找出当天访问次数前十位的IP,并获取该IP来源,并将分析结果发送邮件到指定邮箱. 实现前两 ...
- Spark如何解决常见的Top N问题
需求 假设我们有一张各个产品线URL的访问记录表,该表仅仅有两个字段:product.url,我们需要统计各个产品线下访问次数前10的URL是哪些? 解决方案 (1)模拟访问记录数据 ...
- Asp.net 字体样式
在页面中使用到字体样式 “微软雅黑“,在静态页面中显示正常,但在asp.net页面中字体无效,此时只需把字体修改为“Microsoft YaHei";
- leetcode实现 “10001”+“1011” 返回二进制相加的结果
https://oj.leetcode.com/problems/add-binary/ 实” 1 public class Solution { public String addBinary(St ...
- TCP 的那些事儿(上)
TCP是一个巨复杂的协议,因为他要解决很多问题,而这些问题又带出了很多子问题和阴暗面.所以学习TCP本身是个比较痛苦的过程,但对于学习的过程却能让人有很多收获.关于TCP这个协议的细节,我还是推荐你去 ...
- 上海Uber优步司机奖励政策(1月25日~1月31日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- Apache-POI操作Excel的一些小技巧
Apache-POI操作Excel将合并后的单元格全部填充为相同数据的一个实例. private static void fillMergedRegion(final Sheet sheet) { f ...
- IntelliJ IDEA自用快捷键 转载
最常用快捷键- 未分类 command Binding Description defeat - Ctrl+/ 代码提示 No - Ctrl+Alt+L 格式化代码 - Ctrl+B 快速打开光标 ...
- [kuangbin带你飞]专题十 匹配问题 二分图多重匹配
二分图的多重匹配问题不同于普通的最大匹配中的"每个点只能有最多一条边" 而是"每个点连接的边数不超过自己的限定数量" 最大匹配所解决的问题一般是"每个 ...
- CMDLINE的解析
在linux的config文件中有一个特殊的宏定义CMDLINE,以前也一直在使用这个宏的参数,但是真正这个宏的解析和使用却不怎么明确.这次有机会多对它有些了解,不妨把这个浅显的认识说出来,记下来. ...