文章目录
C++的发展路径
C++的发展路径总图示
结构化编程特性的发展
结构化编程特性的发展路径对应总图中的左边的轨迹
- 1953年,John Backus发表了首个高级程序设计语言Fortran
Backus提出:汇编程序是符号化的机器语言程序,本质并未改变,这样不太好
Backus提出speedcoding的概念,目标并不是作为针对计算问题描述的形式,只是为了加速编程,这在移植上带来很大问题
Fortran与纯粹的高级程序设计语言(即与机器无关的语言)之间还是有一些差距的,Fortran的诞生展示了可以用科学界采用的表示方法来描述计算过程 - 1958年开始,人们觉得可以将与机器脱离的语言提升到一个新高度,脱离计算机本身的地方性,将高级程序语言作为科学来研究,制作出通用语言
1958年开始,大家发现程序可以不用机器语言来描述,语言作为独立的学科;并由一批人开始做这方面的研究
– Perlis主持(Backus也有参与),进行程序设计语言的研究,称之为IAL(国际代数语言),致力于让program脱离机器(机器指冯诺伊曼结构的机器);结论是,想脱离机器,就要有compiler(因为只有compiler才能使对程序的描述脱离机器本身
– Dijkstra也参与了上述研究,他设计了compiler
这一研究成果(如上两点所述),最终发展成语言Algol 60
附:Dijkstra还提出GO TO是有害的观点(与Knuth一起) - 1965、1966年,Algol 60在结构化编程方面又进一步发展,诞生了Algol 68(其实在Algol 60与Algol 68之间还有一个Algol W)
Algol 68的诞生有三个重点人物的影响:Knuth,Hoare,Wirth
Wirth,在Algol 60的基础上,在IBM上做了Algol W,Algol W最后走到了Algol 68
Knuth,提出GO TO是有害的,并有论文论证:没有GO TO,依旧可以表示结构化编程
Hoare,进行了操作语义方面的研究
这就是C++语言从Algol 68中继承部分的发展
贴近机器特性的发展
贴近机器特性的发展路径对应总图中的中间轨迹
- BCPL由CPL发展而来
CPL是由剑桥来做的程序设计语言
后来CPL延伸到BCPL,BCPL贴近机器本身,是为了能写出高效程序设计的 - BCPL的一个想法:由于不同计算机的I/O系统差异很大,故I/O不作为语言成分,而是作为Lib成分剥离开来,是程序设计语言有更好的移植性(如cout、cin等,是函数,不是语言成分)
- BCPL后续发展为C,两位科学家Ritchie,Thompson
Thompson,要写Unix操作系统,他发现,BCPL可以胜任因为BCPL贴近机器,但他发现该语言太乱;Thompson去掉了BCPL中他觉得乱的部分发展成B,又与Ritchie一同将它发展成C
C++能从系统程序软件一直写到应用软件,他的这种很强的与底层硬件沟通交流的能力是由BCPL继承来的
从C,与面向对象特征结合形成C with class,再结合结构化部分与其他特性,形成C++
面向对象特性的发展
面向对象特性的发展路径对应总图中的右边轨迹
- 面向对象的首个提出者:Dahl,Nygaard
- 首个面向对象语言:Simula 67,诞生于1967年
- 初衷不是为了解决结构化编程的问题,而是为了解决实际问题时不经意间得到了该思想
- Simula 67的背景
1962年,Nygaard有一个仿真系统项目,要开发一个仿真语言来描述系统
他有两个选择:Fortran与Algol 60
结果选择Algol 60作为描述新系统的Simulation Language的Base,因为Algol 60中已经有了block structure的概念(即局部性的概念),更容易写出更安全的产品
但是Algol 60不能胜任作为仿真语言,需要进行改动:需要打破LIFO(后进先出)原则(这个原则是编译器决定的)(因为实际上的仿真系统中单元的生命周期要看其具体演化,不是由编译器决定,而是由相互作用与外部环境决定)
改进措施:
– 把所能够表示的基本单元封装起来(联想到对象)
– 既然单元生命周期并不是由compiler能决定的,必得有一个能控制单元生命周期的机制:由pointer对应simulation中的单元,来决定它的生命周期
– 既然有不同的个体,那么他们的访问控制也是要区分开的(结构化编程里只有一个访问控制,大家都是公开的)
– 有一个schedule来调度不同个体的运作
实现:
– 做了一个runtime system机制来支持这套体制的运行
– 在Algol 60的基础上做语言扩充,加关键字,又可兼容Algol 60,加个前缀Simula;即语言是在Algol 60基础上扩充,但运行是由一套runtime system机制来支持的,这就是Simula I
做完Simula I后发现还可以继续做,Simula系统的思想还可以用在别的地方
进一步扩充:
– 抽象出公共部分,形成class:在Tony Hoare的帮助下,把描述的个体的公共部分形成了一个抽象层次,叫class和subclass;类和子类就是为了解决复用问题,把公共部分少写代码(并非这些代码不存在,而是由compiler代写)
– 加入inherit
– 提出虚函数
– 加入garbage collector机制
——最后形成了Simula 67 - Simula 67中,基本提出了所有面向对象的概念
- Simula 67影响到之后所有面向对象的语言
- Simula 67没被大范围应用的原因:有严重的问题,设计时没有考虑到工业界的普适情况,runtime会带来很大的效率问题
一些高层特性的发展
CLu、Ada等,代表人物Barbara Liskov
C++的诞生
C++语言由Bjarne Stroustrup设计和发展
史前时期:剑桥大学等PhD.论文
- 源于Stroustrup1979年剑桥大学博士论文的需要,主题为“研究分布式系统的系统软件组织”(即研究如何将计算机任务分成部分,协作完成任务),这需要多个节点的协作,就想到需要一个能描述单个计算个体的语言
可选语言:Pascal、Algol 68、Simula等,选择了Simula - 第一阶段
Simula优点:优秀的组合形式、可读性、支持并发、类型系统、编译捕错能力
但是失败了,因为性能差(来源于runtime的类型检查与废料收集)(Simula支持动态类型)
问题出现点:连接器出现问题
Simula最缺一个高效的Linker
连接器简述
写了程序后,分成两部
- 编译:有一个文件在理论上要对应有一个二进制文件(由source code来,是binary code),但是整个程序的大部分都不是人写的,会有library等
- 连接:靠linker将上面说的所有部分(是自己写的、不是自己写的)连接、组装起来,即Linker将不同组件协同起来形成整体
这阶段(用Simula描述问题的阶段)结论:能够将分布式组织方式的想法设计实现,能看到程序设计的表达,但是没办法度量或得到数据,因为程序跑不起来,系统无法运行
-
第二阶段
用BCPL重写系统:将Simula写好的当作详细设计的伪代码描述和一部分的验证,再由BCPL去写,得到数据,完成论文
这阶段(用BCPL重写系统完成论文)结论:
没有合适的工具,绝不去冲击一个问题
愿望:可以有一个新语言,它是带有Simula类(描述面向对象)的Algol 68(语言的基础),以C为基础实现(用C的compiler和LInker)
思考:
科学观:
– 设计:程序组织
– 效率:连接规则简单、灵活(异构语言)
– 移植性:不能依赖复杂的系统运行
– 其他:protected、const、区分初始化和变量、异常
哲学观、历史观:
– 实用主义
文学观:
– 存在主义
– 幽默感
解释:数据限定正确操作与访问控制——源于OS
区分初始化与赋值
int x = 8; //这是初始化 int x; //这是 x = 8; //赋值
初始化和赋值机制完全不同
结构化编程中似乎看不出区别
面向对象时,初始化调用的是构造函数,赋值调用的是赋值操作符重载函数实用主义与存在主义
存在即有意义
故有些语法成分看起来繁杂、重复甚至矛盾,但是它们都被保留,因为他们能解决问题就是有价值的
不用语言过分约束人的习惯
之后:Bell Lab中的任务
- 第三阶段
带类的C 1979
任务是Unix内核的分布,要做内核模块化、流量分析等
Stroustrup就接触到了C(且与Thompson也有接触),解决了他用BCPL时的一些问题
该任务就用C来实现
为了组织class,采用的方法是:
– 在原来C的基础上,加入关键字class
– 舍弃并行:因为他认为并发性质应属于system
– 舍弃复数、矩阵、字符串等:因为他认为不是所有人都会用到他们
此时他已经有了做通用程序设计语言的潜意识,但是还没走到那一步
采用这一方法,也含有了C的接近问题、接近机器的特点和方法
这个做出来的成果叫CPre(即C with class):
机制是预编译,将新的语言成分先编译成C语言,再由C的编译器与Linker处理,形成机器语言文件去运行
这里没有做新的系统或编译器,因为意图是想依赖比较成熟的C编译器和连接器(高效、可用、可移植)
这阶段(CPre推出并小范围应用后):保留了C的负面影响(C有不合理、不安全之处,但他认为不应该舍弃C的不好之处来付出效率代价,而是应该接受它们);接受它们不一定用它们(如函数调用参数不严格检查);后面的C++:推出更好的机制与旧的并行,有程序员来选择使用
这种观念基于一个观点:程序员是可以信赖的(不好的部分存在,合理性在于相信程序员会做最好的选择) - 为了解决C中不好的部分,进行了以下扩充
由C with class到C++
C with class成功后,进一步想扩充,使C成为Universal Language
进行的工作:
- Linker:连接兼容性与代码兼容性
C++中,每个C模块分别编译;还允许link到其他模块(如可以与Pascal写的module进行link)
要求linker里不能附加任何有关C++的其他内容
类型安全
为了方便与其他语言实现模块连接,有要求如不能附加DB(如散列) - Doug McIlroy加入工作:
提出观点:逐步扩充、每步做测试;每一步扩充先自我应用才能推广到语言中去,且不能牺牲效率 - 1982年,C with class成功走进工业界和大学,有了这些特点,正往Universal Language里走
- Al Aho提了建议:做了CFront(具体机制见下“C++程序的运行”)
- C++在1983年提出
C++程序的运行
C with class程序的运行机制
- C预处理机制
出来的是完整的源代码
进行工作不是编译而是编辑:处理#、define等关键字的内容 - CPe:不懂C语法,只做翻译
将class等新部分翻译成C,交给C Compiler - C Compiler
负责发现错误:发现CPe交给他的翻译完的C程序等错误,若有错再经手返回到源码端显示(比较麻烦而且源码那边不知道是CC错误还是CPe端错误)
C++程序的运行机制
- CFront:懂C语法,对源码做语法检查、优化工作(如添加封装)
发现完整源码有错直接反馈错误,将错误消灭在CFront之前
没有错误后交给CC
这个机制里,东西交给CC后就不回头了 - 为何CFront不直接生成机器码?
想充分利用C的强大的编译器、连接器等 - 现在的流行机制
现在不多用CFront了,而是LLVM(Low Level Virtual Machine)
LLVM做的是通用框架,可以将所有语言都翻译成该高级语言与机器码之间的中间形式,在所有语言代码都可交流的统一中间代码表示阶段做与机器无关的优化和与机器有关的(根据本机器)翻译,再形成机器码
C++小总结
-
图示
注:
新idea要让老程序员验证后再加入语言
正交性:不能矛盾的特性
只要有用,可以让矛盾的成分加入 -
C与C++的关系
C++是C的超集(不准确的说法,如C++就不可以命名名字为class的变量)
C++支持C的全部编程技巧(即C能实现的,C++全能实现)
任何C程序都能被C++用基本相同的方法编写,并具备同等开销(时间、空间) -
禅
Programmer needs to be trusted
与可能出现的错误相比,更重要的是能做好什么事情