概观C程序设计语言.doc_第1页
概观C程序设计语言.doc_第2页
概观C程序设计语言.doc_第3页
概观C程序设计语言.doc_第4页
概观C程序设计语言.doc_第5页
已阅读5页,还剩24页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

From The Handbook of Object Technology (Editor: Saba Zamir). CRC Press LLC, Boca Raton. 1999. ISBN 0-8493-3135-8.An Overview of the C+ Programming Language 概观C+程序设计语言Bjarne StroustrupAT&T LaboratoriesFlorham Park, NJ07932-0971, USA原著者:Bjarne Stroustrup(C+的设计者)翻译者:K N G of rk kingofark的话:翻译这位C+之父的大作真是对我的双重折磨,一来让一向对自己的英语水平颇有信心的我大受打击,二来也揭了我国语水平之“不足为外人道也”的老底。其实这也不是这次翻译才有的体会,早先就认识到了所以忽然有一天发现自己其实是很自私、很无耻的,因为自己做翻译,无非是提高了自己的英语阅读能力;翻译出来的不堪入目的文章,且不说对不对得起原著者了,至少让很多高、低手看了以后被迫给我提意见和建议,最后还是我自己有所收获。吓!这不是自私是什么?这不是无耻又是什么?当我把这篇文章无条件的(即强制性的)呈现给大家的时候,我已经面红耳赤,无地自容所以,请大家唾弃我、批评我、教导我。我活该!真诚的期待大家对我的批评。摘 要本文通过举例,介绍了C+在如下三个方面中的一些关键的概念:C+的设计、C+程序设计以及C+语言的技术性内容。C+是一门部分的面向系统编程、通用目的的程序设计语言;它支持高效的低级运算、数据抽象、面向对象程序设计以及范型程序设计。第一章 简介以及文本概要(Introduction and Overview)C+程序设计语言提供了一种用于计算和存储的模型,这个模型与大多数计算机所使用的计算和存储模型极为接近。C+的语言结构还提供了具有可适应性的、强大的抽象机制,用于对问题进行抽象;这种语言结构允许程序员创建和使用新的型别(type),而这些新的型别则可以与实际应用中所包含的概念相适应。由此可知,C+既支持面向低层次的程序设计风格,又支持面向高层次的程序设计风格。其中,面向低层次的程序设计风格是基于对硬件资源的直接操纵,以此来获得相当高的效率;而面向高层次的程序设计风格则是基于用户自定义的新的型别,以此来提供这样一种数据和计算模型:当完成一项任务时,计算机使用的这种模型与人类自身观察并完成该项任务时所使用的模型极为相似。这些面向高层次的程序设计风格通常被描述为数据抽象、面向对象程序设计以及范型程序设计。本文围绕那些被C+直接支持的主要程序设计风格展开叙述,内容包括:2C+的设计和演化:介绍C+语言的设计目标和其演化发展的原则;3 C程序设计模型:介绍C+所包含的C语言子集以及其它支持传统的系统程序设计风格的语言设施;4 C+的抽象机制:介绍类(class)的概念及用法(即如何定义那些完全可以被当作内建型别来使用的新型别);说明抽象类如何被用来提供各种不同型别的对象之接口;描述在面向对象程序设计中使用到的类层次机制;以及用于支持范型程序设计的模板(template)机制;5 大规模程序设计:描述namespace(名字空间)以及异常处理(exception handling)机制(在将许多独立的部件合成为完整的程序时,用它们来缓减整个过程的难度);6 C+标准程序库:介绍一些标准设施,比如输入/输出流、string、container(例如vector、list和map)、范型算法(例如sort()、find()、for_each()等)以及对数学计算的支持等。最后,本文还介绍了一些使用C+的应用,并给出了进一步阅读的建议。第二章 C+的设计和演化(The Design and Evolution of C+)C+程序设计语言是由来自AT&T Bell Laboratories的Bjarne Stroustrup(即本文作者)设计和实现的,它兼具Simula语言在组织与设计方面的特性以及适用于系统程序设计的C语言设施。C+最初的版本被称作“带类的C(C with classes)”Stroustrup,1980,在1980年被第一次投入使用;当时它只支持系统程序设计(3)和数据抽象技术(4.1)。支持面向对象程序设计的语言设施在1983年被加入C+;之后,面向对象设计方法和面向对象程序设计技术就逐渐进入了C+领域。在1985年,C+第一次投入商业市场Stroustrup,1986 Stroustrup,1986b。在1987至1989年间,支持范型程序设计的语言设施也被加进了C+Ellis,1990Stroustrup,1991。随着若干独立开发的C+实现产品的出现和广泛应用,正式的C+标准化工作在1990年启动。标准化工作由ANSI(American National Standard Institute)以及后来加入的ISO(International Standards Organization)负责。1998年正式发布了C+语言的国际标准C+,1998。在标准化工作进展期间,标准委员会充当了一个重要的角色,其发布的C+标准之草案在正式标准发布之前,一直被作为过渡标准而存在。而作为标准委员会中的积极分子,我是C+进一步发展工作中的主要参与者。与以前的C+语言版本相比,标准C+更接近我理想中的那个C+语言了。关于C+的设计和演化,在Stroustrup,1994、Stroustrup,1996和Stroustrup,1997b中有详细的叙述。至于标准化工作末期产生的C+语言定义,在Stroustrup,1997有详细叙述。21 C+的设计目标(C+ Design Aims)C+的设计目标,就是要让C+既具有适合于系统程序设计的C语言所具有的可适应性和高效性,又能在其程序组织结构方面具有像Simula那样的语言设施(Simula所支持的这种程序组织结构通常被称为面向对象程序设计风格)。在设计的时候,还做了很大的努力,使得引借自Simula的高层次的程序设计技术能够应用于系统程序设计之中。这即是说,C+所提供的抽象机制能够被应用于那些对效率和可适应性具有极高要求的程序设计任务之中。上述的C+之设计目标可以小结如下:设计目标 l对于要解决实际问题的程序员而言,C+使程序设计变得更有乐趣; lC+是一门通用目的的程序设计语言,它:是一个更好的C;支持数据抽象;支持面向对象程序设计;支持范型程序设计。对范型程序设计的支持在C+设计的后期才被作为一个明确、独立的目标来实现。而在C+演化过程的大部分时间里,我一直把范型程序设计以及支持它的语言特性划归在“数据抽象”的大标题之下。22 C+的设计原则(Design Principles)在Stroustrup,1994中,C+的设计规则被分为基本规则、基于设计的规则、语言的技术性规则以及基于低层次程序设计的规则四个方面,分列在下文中。基本规则(General rules) lC+的每一步演化和发展必须是由于实际问题所引起的; lC+是一门语言,而不是一个完整的系统; l不能无休止的一味追求完美; lC+在其存在的“当时”那个时期必须是有用处的; l每一种语言特性必须有一个有根据的、明确的实现方案; l总能提供一种变通的方法; l能为意欲支持的每一种程序设计风格提供易于理解的支持方法; l不强制于人。可以注意到,基本规则的最后三条暗示了两点:对适用于真实世界中各种应用的便捷工具的强调;对程序员的技术和取向(偏好)的充分考虑。从一开始,C+面向的就是那些要做实际项目的程序员。所谓的“完美”被认为是不可能达到的,这是由于C+用户在需求、背景和待解决问题上存在着太大的不同。况且,在一门通用目的的程序设计语言的整个生存期之内,连对“完美”一词的诠释都可能会有极大的改变。由此可知,在语言的演化过程中,来自用户的反馈和语言实现者们积累的经验才是最为重要的。基于设计的规则(Design-support rules) l支持良好的设计方案; l提供用于程序组织的语言设施; l心口如一(Say what you mean); l所有的语言特性必须具有切实有效的承受能力; l开启一个有用的特性比避免所有的误用更为重要; l能将独立开发的部件组合成完整的软件。C+的一个目标就是提供更易用并具有一定承受能力的设计思想和程序设计技术,进一步提高程序的质量。这些技术中的绝大部分都源自Simula Dahl,1970Dahl,1972Birtwistle,1979,并通常被作为面向对象程序设计和面向对象设计思想来讨论。然而,C+的设计目标总还是在于要支持一定范围内的各种程序设计风格和设计思想。这与一般在语言设计方面的观点形成一定对比。一般在语言设计上总是试图将所有系统内建于单独一个被重点支持的、带有强制性的程序设计风格之中(或称典范paradigm)。语言的技术性规则(Language-technical rules) l与静态型别系统(Static type system)没有内在的冲突; l像对内建(built-in)型别一样对用户自定义型别提供很好的支持; l个异化(locality)行为是可取的; l避免产生顺序上的依赖关系; l在对语言产生疑惑时,可以选取其特性中最易掌握的部分; l可以因为不正当的语法使用而产生问题(Syntax matters (often in perverse ways)) l削弱对预处理器的使用。当然,这些规则要具体结合更多关于基本目标的上下文环境来考虑。应该注意到的是,在“与C有较高的兼容性”、“不损失效率”以及“具有便捷的可用性来解决实际问题”这三个方面的要求,与在“完整的型别安全性”、“完全的通用性”以及“完善的抽象之美”这三个方面的要求形成对立。C+从Simula中借鉴了用户自定义型别(class,4.1)和类层次机制。然而,在Simula及许多类似的语言中,其对用户自定义型别的支持与其对内建型别的支持存在着根本上的不同。例如,Simula中不允许在栈中为用户自定义型别的对象分配空间,并且只允许通过指针(这在Simula中称为引用reference)来对这些对象进行访问。而相反的,内建型别的对象只在栈中被分配空间,不能在动态存储区中分配,而且不能使用指针指向它。这种在对待内建型别与对待用户自定义型别上的差异,暗示着对效率问题的严格考虑。比如,当作为一个在动态存储区中被分配的对象之引用时,如果该对象属于自定义型别(比如complex,4.1),那么就会为运行期及空间带来负荷;而这些负荷在有些应用中被认为是不可接受的。这些正是C+意欲涉足解决的问题。同时,在用法上的不同也决定了:不可能在范型程序设计中统一对待那些语义上近似的型别。在维护一个较庞大的程序时,一个程序员不可避免的会基于某些不完整的知识来对程序作一些修改,只关注全部程序代码中的一小部分。基于此,C+提供了class(4)、namespace(5.2)和访问控制(4.1),使设计决策的各异化(locality)成为可能。在基于一趟编译(one-pass compilation)的语言中,某些顺序上的依赖性是不可避免的。例如在C+中,一个变量或者函数在其被声明之前是无法使用的。然而,C+中类成员的名字规则和重载解析(overload resolution)的规则还是在独立于声明顺序的原则下被制定出来,以便将发生混乱和错误的可能性降至最低。基于低层次程序设计的规则(Low-level programming support rules) l使用传统的(笨拙的)连接器(linker); l与C语言不存在无故的不兼容性; l不给C+之下层级的更低层语言留出余地(汇编语言除外); l你不会为你所不使用的部分付出代价(零负荷规则); l在产生疑惑时,能提供完全自主控制的途径。在C+的设计中只要在不严重影响其对强型别检查(strong type checking)的支持的地方,都尽量做到与C的“source-link”方式相兼容。除了某些微小的细节差别之外,C+将CKernighan,1978Kernighan,1988作为一个子集包含了进来。C+与C的兼容性使得C+程序员立刻就能有一个完整的语言和工具集可用。还有两点也很重要,一是有大量关于C的高质量的教学素材已经存在,二是C+程序员可以利用C+与C的兼容性而直接并有效的使用大量现成的程序库。在决定将C作为C+的基础的时候,C还没有像后来那样出类拔萃、炙手可热,所以在考虑这个问题的时候,与C语言所提供的可适应性和高效性相比,C语言的流行程度只是个次要的考虑因素。然而,与C的兼容性也使得C+在某些语法和语义上保留了C的一些瑕疵之处。比如,C语言的声明语法就实在远不及优美;而其内建型别的隐式转换规则也是混乱无章法的。还有另一个大问题,就是许多从C转向C+的程序员并没有认识到,代码质量上的显著提高只能通过在程序设计风格上的显著改变来达到。第三章 C程序设计模型(The C Programming Model)计算机中有一个被广泛使用的基本概念一直是保持固定不变的:存储区(memory)是word或byte的序列,通过被称为地址的整数来进行索引。现代的计算机即近20年内设计的计算机都倾向于对函数调用使用栈的方法进行直接的支持;而更为甚之的是,所有流行的机器都有一些诸如输入、输出之类的重要设施,这些设施与传统的面向word或byte的计算和存储模型并不能很好的相适应。这些设施总是需要通过特殊的机器指令或者特别的语义来访问存储区。而从一个高级语言的角度来看,不管用那种方法,对这些设施的使用者而言都是麻烦的,而且与特定机器的体系结构有关。在对这方面的处理上,C是最为成功的一门语言,它所提供的程序设计模型能够与机器包含的模型很好的相适应。C提供独立于机器体系结构的、语言层面的方法;这种方法能与关键的硬件方法相对应:字符用byte,整数用word,指针用于地址,函数用于对程序的抽象。C还摒弃了一些在这方面具有约束性的语言特性,使得程序员可以在必要时亲自操纵特定的硬件层细节。在那些依赖并利用机器的某些特定设计而得到益处的领域里,C就显得相对更容易学和更容易使用一些。更何况,C是如此容易实现它几乎已经变得随处可得了。31 数组和指针(Arrays and Pointers)在C中,一个数组只是一片存储区域。例如:int v10;/ 10个int型变量的数组v3 = 1; / 将1赋值给v3int x = v3; / 从v3读取元素值表示下标的标记 用在声明中,表示声明的对象是数组;用在表达式中,表示数组的元素。一个C语言中的指针是指一个变量,它存放着某个存储位置的地址。例如:int* p;/ p是一个指向int型对象的指针p = &v7;/ 将v7的地址赋给p*p = 4;/ 通过p来向v7写入数据int y = *p;/ 通过p来读取v7的数据 指针的提领(dereference,“指向”的意思)标记 * 用在声明中,表示声明的对象是指针;用在表达式中,表示取指针所指向的那个元素。这可以用下图表示:C+引借了C的这种颇为简单的、与机器结构极为近似的存储方案,同时也引借了C在表达式、控制结构以及函数等方面所使用的方案。例如,我们可以像下面这样写一个函数,其功能是在vector里查找一个元素并返回一个指向匹配元素的指针:int* find(int v, int vsize, int val)/在v中查找valfor(int i = 0; i 1) return f*fact(f-1);else return 1;每一个被单独编译的程序段都有一个接口,这个接口包含了使用这个程序段所需的尽量少但足够用的信息。对于fact.c这个程序段而言,这个接口包含了fact()函数的声明,声明被放在fact.h文件中:/fact.h文件long fact(long);每一个需要使用这个程序段的翻译单元都用#include语句将这个接口包含进来。此外,我还更倾向于在翻译单元中用#include包含另一个接口,使得编译器能够更早的对不完整性进行诊断。我们可以像这样使用fact()函数:/main.c文件#include “fact.h”#include int main() std:cout “factorial(7) is “ fact(7) n; return 0;main()函数是程序的起始点;iostream属于标准C+输入/输出程序库;std:cout是标准的字符输出流(6.1)。运算符 chunk_size) if (d-write(p, chunk_size) = chunk_size() / 对整个chunk写 size -= chunk_size; /写入了chunk_size个字符 p += chunk_size; /移到下一个chunk else /对部分chunk施以写操作 / / 真正的驱动器将会在派生自Character_device的类中被具体实现:class Dev1:public Character_device / 对Dev1的表述public:int open(int opt); / 打开Dev1int close(int opt); / 关闭Dev1int read(char* p, int n); / 读取Dev1/ ;class Dev2:public Character_device / 对Dev2的表述public:int open(int opt); / 打开Dev2int close(int opt); / 关闭Dev2int read(char* p, int n); / 读取Dev2/ ;各个类之间的关系可用下图表示:图中的箭头代表“派生自”的关系。用户的user()函数不需要了解到底是哪一个实现了Character_device之接口的派生类被使用。void f(Dev1& d1, Dev2& d2, char* buf, int s)user(d1, buf, s);/使用Dev1user(d2, buf, s);/使用Dev2在一个派生类中声明的函数会覆写(override)其基类中的一个同名且同型别的函数。由C+语言本身来保证:对Character_device中诸如write()这样的虚拟函数之调用确实唤起了(invoke)来自实际被使用的相应派生类的覆写函数。在C+中,这样做所带来的负荷已被尽量减到了最小,并可以被精确的预见。由虚拟函数引起的额外的运行期负荷也只占用普通函数调用之消耗的一小部分而已。下图显示了类中各个对象的典型实现:由此可以看出,对虚拟函数的调用只不过是对普通函数的一种间接调用。在运行期间,并不需要为调用正确的函数版本而进行某种搜索。在许多具体情况中,使用抽象类是表述一个系统主要内部接口的理想方法。这种方法简单、高效,具有“强型别”(strong type)特性,使得“同时使用同一接口的不同实现方案”成为可能,并且能将这些实现中的任何改变所产生的影响与用户完全隔离开来。421析构函数(Destructors)对于一个给定的对象,构造函数为类中的成员函数建立了一种“工作环境”。通常,要建立这种“工作环境”需要获取一些资源(比如内存、锁或者文件等)。一个程序要正常运作,还需要在对象被销毁的时候能正常的释放这些资源。因而,有必要声明一个函数,让其实现与构造函数相反的功能。这样的函数被顺理成章的称为析构函数(译注:之所以说“顺理成章”,是因为英文中con-structor 和de-structor这两个单词的拼法是遵循同一规则的,de-structor系根据con-structor创造出来的,因而在以英语为母语的人眼里,这是很自然的一种词语派生方法)。对于一个类X,其析构函数的名称就是X();在C+中,是求补运算符。一个用来存放字符的简单的栈可以像这样定义:class Stack char* v;int max_size;int top;public:Stack(int s) top = 0; v = new Tmax_size = s; /构造函数获取空间Stack() delete v; /析构函数释放空间void push(T c) vtop+ = c; T pop() return v-top; 为了举例子简单起见,在这个Stack类中没有提供任何错误处理的功能。然而,我们仍然可以像这样使用:void f(int n)stack s2(n); / n个字符的栈s2.push(a);s2.push(b);char c = s2.pop();/ 在f()函数的起始处,为了创建s2,调用了构造函数Stack:Stack()。该构造函数为n个字符分配足够的内存。当要退出f()函数时,析构函数Stack:Stack()被隐式的调用,释放了先前由构造函数所获得的内存。采用这种资源管理方案是很重要的。因为,诸如Character_device这样的抽象类之对象将会经由指针或引用被操纵,并且通常会在某些函数里被删除,而这些函数往往并不知道这个抽象类的接口具体是被什么型别的对象实现的。因此,我们不能指望Character_device的使用者能够了解具体需要用什么来释放一个设备。一般来说,释放一个设备涉及到与操作系统或其它系统资源之维护程序的交互。而将Character_device的析构函数声明为virtual则可以保证:Character_device型对象的删除工作是由来自相应的派生类之相应函数完成的。例如:void some_user(Character_device* pd)/delete pd; /隐式的调用对象的析构函数43 面向对象程序设计(Object-Oriented Programming)面向对象程序设计涉及到一系列技术,这些技术基于类层次机制,提供可扩展性和可适应性。面向对象程序设计使用到的基本语言设施包括从一个类派生出另一个类的能力、虚拟函数(详见4.2)以及用户自定义型别。这些特性使得程序员可以在不知道接口内部具体实现的情况下使用这个接口(这里说的“接口”即是指类,且通常是抽象类),并且可以在不影响原来的类之使用者的情况下,直接在原来的类之基础上建立新的类。举个例子来说:考虑一个简单的任务,其目标是通过某种用户接口系统获取来自用户的一个整型值,并将其传给应用程序。假设我们希望使应用程序独立于用户接口的实现细节,于是我们可以提供一个Ival_box类来作为交互的手段:class Ival_box public:virtual int get_value() = 0;/ 将数值取回应用程序virtual void prompt() = 0;/ 提示用户输入/ ;显然,可能会有各种属于Ival_box型别的新型别出现:class Ival_dial:public Ival_box /* */ ;class Ival_slider:public Ival_box /* */ ;/ 这几个类之间的关系可以用下图表示:这个应用层次(application hierarchy)独立于用户接口系统的实现细节。应用程序的编写独立于输入/输出的实现细节;在不影响应用层次的情况下,我们可以将应用程序加入到实现层次当中(implementation hierarchy):虚线箭头代表着protected抽象类。protected抽象类是其派生类之实现的一部分,通用的用户代码无法对其进行访问。这种设计使得应用程序的代码独立于实现层次,实现层次的改动不会影响到应用程序代码。出于现实因素的考虑,我在代码里的名称中使用了BB这个前缀;因为现今各主要程序库大凡都采用添加易识别的标志这样一种传统方式来增加可读性和易辨识性。更好的替代方案是使用namespace关键字(5.2)。将应用程序的类加入实现层次的类声明,其一般是像下面这样的:class BB_ival_slider:public ival_slider, protected BB_slider public:/ 在这里,我们根据实现应用程序特定概念的需要,对Ival_slider的函数进行覆写protected:/ 为了符合用户接口的标准,这里的函数覆写了BB_slider和BB_window的函数private:/这里是型别的表述和其它具体实现细节;这种结构通过覆写BB_window层次结构中的虚拟函数来表现用户接口系统要显示的细节内容。对一个用户接口系统而言,这也许并不是一种理想的组织结构,但好在这种结构并不常见。派生类会继承其基类的属性。因此,派生有时候也被称为“继承”。当一种语言(比如C+)允许一个类直接拥有多个基类的时候,我们就说这种语言支持多重继承。431 运行期型别识别(Run-time Type Identification)在上面定义的Ival_box的一种可行的使用方法就是:在应用程序中将Ival_box对象

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论