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两个月以上的朋友基本上都能自己解决了,因为太经典了, ...
随机推荐
- C++11并发内存模型学习
C++11标准已发布多年,编译器支持也逐渐完善,例如ms平台上从vc2008 tr1到vc2013.新标准对C++改进体现在三方面:1.语言特性(auto,右值,lambda,foreach):2.标 ...
- Google Map API 学习四
- php 错误信息配置
display_errors = on/off错误回显,一般常用语开发模式,但是很多应用在正式环境中也忘记了关闭此选项.错误回显可以暴露出非常多的敏感信息,为攻击者下一步攻击提供便利.推荐关闭此选项. ...
- css背景图片位置:background的position
position的两个参数:水平方向的位置,垂直方向的位置----------该位置是指背景图片相对于前景对象的 1.background:url(../image/header.jpg) no-re ...
- lvs,haproxy,keepalived,heartbeat
lvs的是通过vrrp协议进行数据包转发的,提供的是4层的负载均衡.特点是效率高,只要你机器网卡抗的住就不是问题. haproxy可以提供4层或7层的数据转发服务,能做到7层的好处是可以根据服务所处的 ...
- 在Ubuntu中设置中文输入法
在Ubuntu中设置中文输入法 */--> pre { background-color: #2f4f4f;line-height: 1.6; FONT: 10.5pt Consola,&quo ...
- 不使用OCI8接口如何连接PHP和Oracle
随着网站规模的扩大,MySql显然不能满足需求,在许多网站都 采用大型数据库Oracle的情况下,如何使用PHP来访问Oracle变的越发重要了. 我从我编写的一个简单iERP系统谈我自己是如何做 ...
- JVM性能调优-GC内存模型及垃圾收集算法
JVM内存管理模型: http://developer.51cto.com/art/201002/184385.htm 一 JVM内存模型 1.1 Java栈 Java栈是与每一个线程关联的,JVM在 ...
- 向MyEclipse中导入项目要注意的问题
如何导入 修改项目名称(路径) 修改类库 如何导入: 右键Package Explorer -> Import 如果是把别人的项目拷贝到自己的工程中,而且又改了项目名称,那么发布之前一定要改一个 ...
- 3D视频的质量评价报告 (MSU出品)
俄罗斯的MSU Graphics & Media Lab (Video Group)出品的3D视频的质量评价报告.测试了一些3D视频的质量,其测试方法值得我们参考.在此翻译一下部分文字. 注: ...