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

上面xm都是“直接变量”,它们在内存中的区域可以不同:可以在程序的静态空间(全程变量),可以在工作堆栈内(自动变量),也可在动态空间内(间接地包含在母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

我们看到,上面的程序显示,rx 几乎没任何区别,好像是形影相随,难分难舍。这正是C++的宗旨:让reference的变量和直接变量具有“等同”的语法,用法。

作为初级程序员来说,知道到这个用法也就够了,但是“一点点知识是危险的”。如果我们以为xr 是一个东西,那就错了:

首先,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的用法:

因为对一个指针赋值会使得它改变指向。若为指针,line4 会使得 指向地址为 的内存元,这和 alias 的概念又风马牛不相及。

幸运的是实际情况并非如此:没变(指向没变),倒是 变了,在这个用例中,对 的 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 的“三位一体”诠释的更多相关文章

  1. 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 ...

  2. 【转】Django Model field reference学习总结

    Django Model field reference学习总结(一) 本文档包含所有字段选项(field options)的内部细节和Django已经提供的field types. Field 选项 ...

  3. (转) Qt 出现“undefined reference to `vtable for”原因总结

    由于Qt本身实现的机制所限,我们在使用Qt制作某些软件程序的时候,会遇到各种各样这样那样的问题,而且很多是很难,或者根本找不到原因的,即使解决了问题,如果有人问你为什么,你只能回答--不知道. 今天我 ...

  4. 重新诠释的OSGi规范

    上周五部门开会讨论新一代产品(基于.net Winform)的设计规范,从设计规范慢慢讨论到体系结构等架构存在的问题,诸如菜单.工具条.状态条.界面布局等不能实现配置化和自动化,子系统之间拥有强依赖, ...

  5. undefined reference to `__android_log_print'

    使用android studio 编写NDK代码时出现错误:undefined reference to `__android_log_print' 解决办法: eclipse       andro ...

  6. 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 ...

  7. Qt - 错误总结 - 在自定义类头文件中添加Q_OBJECT 编译时报错(undefined reference to ‘vtable for xxThread)

    错误提示:在添加的QThread子类头文件添加Q_OBJECT时,编译程序,出现"undefined reference to 'vtable for xxThread'"错误提示 ...

  8. 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 ...

  9. Qt经典出错信息之undefined reference to `vtable for classname

    原文链接:Qt经典出错信息之undefined reference to `vtable for classname 这个出错信息太常见了,用过Qt两个月以上的朋友基本上都能自己解决了,因为太经典了, ...

随机推荐

  1. Nmap 使用指南

    Namp的英文全称是“Network Mapper”,Namp可用来快速扫描大型网络或是单个主机 nmap主要包括四个方面  主机发现.端口扫描.应用与版本侦测.操作系统侦测 主机发现原理:主机发现的 ...

  2. Ogre参考手册(五)3.2 合成器

    3.2 合成器Compositor 合成器框架是Ogre用于全屏后处理的API.你可以通过脚本而不是API定义合成器.你可以很容易为视口实例化合成器. 合成器基础 无论是要替换还是要与主渲染窗口混合, ...

  3. 【如何快速的开发一个完整的 iOS 直播 app】(美颜篇)

    来源:袁峥Seemygo 链接:http://www.jianshu.com/p/4646894245ba 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播 ...

  4. CDH CM安装及简单群集部署测试

    前吃人的故事开始了,金钱是如何吃人的呢?我在想ing,还没想通,一起吧,哈哈: 入题,别胡扯,误人子弟!!!! CM@@!!!!!!....................., 先来张monitor ...

  5. 原生JS实现圆周运动

    <body> <div id="ball" style="width:20px; height:20px; background:red; border ...

  6. js 逻辑运算符优化

    运算符的代码优化,可以精简代码,提高代码可读性 下面主要讨论下逻辑运算符与 &&, 或||. 示例: 假设对成长速度显示规定如下: 成长速度为5显示1个箭头: 成长速度为10显示2个箭 ...

  7. Java EE JAR包的说明

    在java ee的开发中,jar文件是工程的基础,下面转载了网上兄弟一篇文章,简单介绍了一下,java ee中常用的jar文件的说明: activation.jar 与javaMail有关的jar包, ...

  8. libev学习之ev_run

    好吧,神马都init好了,loop毕竟是个环呐,在哪跑起来呢,ok,他是ev_run的工作: int ev_run (EV_P_ int flags) { #if EV_FEATURE_API ++l ...

  9. Python3 Tkinter-Pack

    1.创建 from tkinter import * root=Tk() print(root.pack_slaves()) Label(root,text='pack').pack() print( ...

  10. Winform判断是否已启动

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.W ...