C++Reference 的“三位一体”诠释

Malina ·
更新时间:2024-09-21
· 899 次阅读

  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++程序,有着不容忽略的负面影响。



三位一体 c+

需要 登录 后方可回复, 如果你还没有账号请 注册新账号