- 141.84 KB
- 2022-04-21 发布
- 1、本文档由用户上传,淘文库整理发布,可阅读全部内容。
- 2、本文档内容版权归属内容提供方,所产生的收益全部归内容提供方所有。如果您对本文有版权争议,请立即联系网站客服。
- 3、本文档由用户上传,本站不保证质量和数量令人满意,可能有诸多瑕疵,付费之前,请仔细阅读内容确认后进行付费下载。
- 网站客服QQ:403074932
目录C++基础教程简介2第一章C++基础知识(BasicsofC++)31.1C++程序结构(Structureofaprogram)41.2变量和数据类型(VariablesandDatatypes)81.3常量(Constants)171.4操作符/运算符(Operators)21第二章控制结构和函数382.1控制结构(ControlStructures)382.2函数I(FunctionsI)482.3函数II(FunctionsII)532.4参数的默认值(Defaultvaluesinarguments)562.5函数重载(Overloadedfunctions)572.6Inline函数(inlinefunctions)582.7递归(Recursivity)592.8函数的声明(Declaringfunctions)60第三章高级数据类型(AdvancedData)623.1数组(Arrays)623.2字符序列(CharacterSequences)693.3指针(Pointers)763.4动态内存分配(Dynamicmemory)873.5数据结构(DataStructures)923.6自定义数据类型(Userdefineddatatypes)100第四章面向对象编程1044.1类(Classes)1054.2操作符重载(Overloadingoperators)1164.3类之间的关系(Relationshipsbetweenclasses)123177/177n4.4多态(Polymorphism)132第五章C++高级1405.1模板(Templates)1405.2名空间(Namespaces)1495.3出错处理(Exceptionhandling)1545.4类型转换高级(AdvacnedClassType-casting)1585.5预处理指令(PreprocessorDirectives)163第六章C++标准函数库168177/177nC++基础教程Beta版原作:JuanSoulié翻译:JingXu(aqua)英文原版本教程根据JuanSoulie的英文版C++教程翻译并改编。本版为最新校对版,尚未定稿。如有不明或错误之处,请参考英文原版,并敬请在本站留言指正。版权归作者所有,欢迎链接,请勿转载。本教程对C++语言进行了深入浅出的介绍,从基础知识到ANSI-C++标准的最新功能,内容涵盖了从数组,类等基本概念到多态、模板等高级概念。教程本着实用的原则,每一小节都结合了可以工作的程序实例,以便读者从第一课开始就可以上手实习。本翻译版本对许多C++概念中的关键词保留了中英文对照,以便读者增强理解,并方便日后阅读英文原版教材C++基础教程简介怎样使用本教程读者范围本教程面向所有希望学习C++语言的读者。如果读者有其他编程语言背景或计算机相关基本知识可以帮助更好的理解教程内容,但这并非必须条件。对于C语言熟悉的读者可将前三章(1.1到3.4)当作复习,因为这部分内容主要介绍C++中的C部分。不过某些C++的语法与C还是有些差别,所以建议还是快速的读一下这部分。第四章讲述面向对象编程。第五章主要介绍ANSI-C++标准中的新增的功能。本教程结构教程共分6章,每章分若干小节。你可以直接从主目录进入任意小节,并循每页底部的链接向后浏览。很多小节含有一页例题介绍该章节主要知识点的使用。建议在进入下一章学习之前最好先阅读这些例题,理解每行代码。177/177n学习和练习一种编程语言的最好办法是自己修改书中例题程序,设法在程序中增加新的功能。不要不敢修改这些例题程序,这正是学习的方法。兼容性备注ANSI-C++标准近几年来被接受为国际标准。尽管C++语言从二十世纪80年代即存在,ANSI-C++在1997年才被发表,2003年又被修订过。因此很多编译器不支持ANSI-C++中的部分新功能,特别是那些在此标准发表前即被发布的编译器。在本教程中,那些ANSI-C++中新增的而老一代C++编译器大多不支持概念将备用如下标志标出:ANSIC++新增的概念同样对于C和C++在实现上有明显不同的概念,将备用如下标志标出:C与C++不同的地方编译器本教程中所有例题程序均为console程序(控制台程序)。此类程序以文本形式与用户交换信息,显示结果。所有C++编译器均支持console程序的编译。要了解更多关于如何编译的说明,请查询你的编译器用户使用手册。C++编译器和开发环境推荐很多读者询问编译器和开发环境的问题。除了常用的商用收费的MSVisualStudio,VC++,BorlandC++等工具外,还有很多免费的工具也是很好用的。这里推荐两种免费的C++开发软件:1、Eclipse的CDT开发工具,官方网站在http://www.eclipse.org/cdt/2、开源工具Dev-C++和wxDev-C++第一章C++基础知识(BasicsofC++)1.C++程序结构Structureofaprogram177/177n2.变量和数据类型VariablesandDatatypes3.常量Constants4.操作符/运算符Operators5.控制台交互Communicationthroughconsole1.1C++程序结构(Structureofaprogram)下面我们从一个最简单的程序入手看一个C++程序的组成结构。//myfirstprograminC++#includeusingnamespacestd;intmain(){cout<<“HelloWorld!”;return0;}HelloWorld!上面左侧显示了我们的第一个程序的源代码,代码文件名称为hellowworld.cpp。右边显示了程序被编译执行后的输出结果。编辑和编译一个程序的方法取决于你用的是什么编译器,根据它是否有图形化的界面及版本的不同,编译方法也有可能不同,具体请参照你所使用的编译器的使用说明。以上程序是多数初学者学会写的第一个程序,它的运行结果是在屏幕上打出”HelloWorld!”这句话。虽然它可能是C++可写出的最简单的程序之一,但其中已经包含了每一个C++程序的基本组成结构。下面我们就逐个分析其组成结构的每一部分://myfirstprograminC++177/177n这是注释行。所有以两个斜线符号(//)开始的程序行都被认为是注释行,这些注释行是程序员写在程序源代码内,用来对程序作简单解释或描述的,对程序本身的运行不会产生影响。在本例中,这行注释对本程序是什么做了一个简要的描述。#include以#标志开始的句子是预处理器的指示语句。它们不是可执行代码,只是对编译器作出指示。在本例中这个句子#include告诉编译器的预处理器将输入输出流的标准头文件(iostream.h)包括在本程序中。这个头文件包括了C++中定义的基本标准输入-输出程序库的声明。此处它被包括进来是因为在本程序的后面部分中将用到它的功能。usingnamespacestd;C++标准函数库的所有元素都被声明在一个名空间中,这就是std名空间。因此为了能够访问它的功能,我们用这条语句来表达我们将使用标准名空间中定义的元素。这条语句在使用标准函数库的C++程序中频繁出现,本教程中大部分代码例子中也将用到它。intmain()这一行为主函数(mainfunction)的起始声明。mainfunction是所有C++程序的运行的起始点。不管它是在代码的开头,结尾还是中间–此函数中的代码总是在程序开始运行时第一个被执行。并且,由于同样的原因,所有C++程序都必须有一个mainfunction。main后面跟了一对圆括号(),表示它是一个函数。C++中所有函数都跟有一对圆括号(),括号中可以有一些输入参数。如例题中显示,主函数(mainfunction)的内容紧跟在它的声明之后,由花括号({})括起来。cout<<“HellowWorld!”;这个语句在本程序中最重要。cout是C++中的标准输出流(通常为控制台,即屏幕),这句话把一串字符串(本例中为”HelloWorld”)插入输出流(控制台输出)中。cout在的声明在头文件iostream.h中,所以要想使用cout必须将该头文件包括在程序开始处。注意这个句子以分号(;)结尾。分号标示了一个语句的结束,C++的每一个语句都必须以分号结尾。(C++程序员最常犯的错误之一就是忘记在语句末尾写上分号)。return0;177/177n返回语句(return)引起主函数main()执行结束,并将该语句后面所跟代码(在本例中为0)返回。这是在程序执行没有出现任何错误的情况下最常见的程序结束方式。在后面的例子中你会看到所有C++程序都以类似的语句结束。你可能注意到并不是程序中的所有的行都会被执行。程序中可以有注释行(以//开头),有编译器预处理器的指示行(以#开头),然后有函数的声明(本例中main函数),最后是程序语句(例如调用cout<<),最后这些语句行全部被括在主函数的花括号({})内。本例中程序被写在不同的行中以方便阅读。其实这并不是必须的。例如,以下程序intmain(){cout<<"HelloWorld";return0;}也可以被写成:intmain(){cout<<"HelloWorld";return0;}以上两段程序是完全相同的。在C++中,语句的分隔是以分号(;)为分隔符的。分行写代码只是为了更方便人阅读。以下程序包含更多的语句://mysecondprograminC++#includeintmain(){cout<<"HelloWorld!";cout<<"I'maC++program";return0;}HelloWorld!I'maC++program177/177n在这个例子中,我们在两个不同的语句中调用了cout<<函数两次。再一次说明分行写程序代码只是为了我们阅读方便,因为这个main函数也可以被写为以下形式而没有任何问题:intmain(){cout<<"HelloWorld!";cout<<"I'mtoC++program";return0;}为方便起见,我们也可以把代码分为更多的行来写:intmain(){cout<<"HelloWorld!";cout<<"I'maC++program";return0;}它的运行结果将和上面的例子完全一样。这个规则对预处理器指示行(以#号开始的行)并不适用,因为它们并不是真正的语句。它们由预处理器读取并忽略,并不会生成任何代码。因此他们每一个必须单独成行,末尾不需要分号(;)注释(Comments)注释(comments)是源代码的一部分,但它们会被编译器忽略。它们不会生成任何执行代码。使用注释的目的只是使程序员可以在源程序中插入一些说明解释性的内容。C++支持两中插入注释的方法://linecomment/*blockcomment*/第一种方法为行注释,它告诉编译器忽略从//开始至本行结束的任何内容。第二种为块注释(段注释),告诉编译器忽略在/*符号和*/符号之间的所有内容,可能包含多行内容。在以下我们的第二个程序中,我们插入了更多的注释。177/177n/*mysecondprograminC++withmorecomments*/#includeintmain(){cout<<"HelloWorld!";//saysHelloWorld!cout<<"I'maC++program";//saysI'maC++programreturn0;}HelloWorld!I'maC++program如果你在源程序中插入了注释而没有用//符号或/*和*/符号,编译器会把它们当成C++的语句,那么在编译时就会出现一个或多个错误信息。1.2变量和数据类型(VariablesandDatatypes)你可能觉得这个“HellowWorld”程序用处不大。我们写了好几行代码,编译,然后执行生成的程序只是为了在屏幕上看到一句话。的确,我们直接在屏幕上打出这句话会更快。但是编程并不仅限于在屏幕上打出文字这么简单的工作。为了能够进一步写出可以执行更有用的任务的程序,我们需要引入变量(variable)这个的概念。让我们设想这样一个例子,我要求你在脑子里记住5这个数字,然后再记住2这个数字。你已经存储了两个数值在你的记忆里。现在我要求你在我说的第一个数值上加1,你应该保留6(即5+1)和2在你的记忆里。现在如果我们将两数相减可以得到结果4。所有这些你在脑子里做的事情与计算机用两个变量可以做的事情非常相似。同样的处理过程用C++来表示可以写成下面一段代码:a=5;b=2;a=a+1;result=a-b;177/177n很明显这是一个很简单的例子,因为我们只用了两个小的整数数值。但是想一想你的电脑可以同时存储成千上万这样的数值,并进行复杂的数学运算。因此,我们可以将变量(variable)定义为内存的一部分,用以存储一个确定的值。每一个变量(variable)需要一个标识,以便将它与其他变量相区别,例如,在前面的代码中,变量标识是a,b,和result。我们可以给变量起任何名字,只要它们是有效的标识符。标识(Identifiers)有效标识由字母(letter),数字(digits)和下划线(_)组成。标识的长度没有限制,但是有些编译器只取前32个字符(剩下的字符会被忽略)。空格(spaces),标点(punctuationmarks)和符号(symbols)都不可以出现在标识中。只有字母(letters),数字(digits)和下划线(_)是合法的。并且变量标识必须以字母开头。标识也可能以下划线(_)开头,但这种标识通常是保留给为外部连接用的。标识不可以以数字开头。必须注意的另一条规则是当你给变量起名字时不可以和C++语言的关键字或你所使用的编译器的特殊关键字同名,因为这样与这些关键字产生混淆。例如,以下列出标准保留关键字,他们不允许被用作变量标识名称:asm,auto,bool,break,case,catch,char,class,const,const_cast,continue,default,delete,do,double,dynamic_cast,else,enum,explicit,extern,false,float,for,friend,goto,if,inline,int,long,mutable,namespace,new,operator,private,protected,public,register,reinterpret_cast,return,short,signed,sizeof,static,static_cast,struct,switch,template,this,throw,true,try,typedef,typeid,typename,union,unsigned,using,virtual,void,volatile,wchar_t,while另外,不要使用一些操作符的替代表示作为变量标识,因为在某些环境中它们可能被用作保留词:and,and_eq,bitand,bitor,compl,not,not_eq,or,or_eq,xor,xor_eq你的编译器还可能包含一些特殊保留词,例如许多生成16位码的编译器(比如一些DOS编译器)把far,huge和near也作为关键字。非常重要:C++语言是“大小写敏感”(“casesensitive”)的,即同样的名字字母大小写不同代表不同的变量标识。因此,例如变量RESULT,变量result和变量Result分别表示三个不同的变量标识.177/177n基本数据类型(FundamentalDatatypes)编程时我们将变量存储在计算机的内存中,但是计算机要知道我们要用这些变量存储什么样的值,因为一个简单的数值,一个字符,或一个巨大的数值在内存所占用的空间是不一样的。计算机的内存是以字节(byte)为单位组织的。一个字节(byte)是我们在C++中能够操作的最小的内存单位。一个字节(byte)可以存储相对较小数据:一个单个的字符或一个小整数(通常为一个0到255之间的整数)。但是计算机可以同时操作处理由多个字节组成复杂数据类型,比如长整数(longintegers)和小数(decimals)。以下列表总结了现有的C++基本数据类型,以及每一类型所能存储的数据范围:数据类型(DATATYPES)名称字节数*描述范围*char1字符(character)或整数(integer),8位(bits)长有符号(signed):-128到127无符号(unsigned):0到255shortint(short)2短整数(integer)16位(bits)长有符号(signed):-32768到32767无符号(unsigned):0到65535longint(long)4长整数(integer)32位(bits)长有符号(signed):-2147483648到2147483647无符号(unsigned):0到4294967295int4整数(integer)有符号(signed):-2147483648到2147483647无符号(unsigned):0到4294967295float4浮点数(floatingpointnumber)3.4e+/-38(7个数字(7digits))double8双精度浮点数(doubleprecisionfloatingpointnumber)1.7e+/-308(15digits)longdouble8长双精度浮点数(longdoubleprecisionfloatingpointnumber)1.7e+/-308(15digits)177/177nbool1布尔Boolean值。它只能是真(true)或假(false)两值之一。true或falsewchar_t2宽字符(Widecharacter)。这是为存储两字节(2bytes)长的国际字符而设计的类型。一个宽字符(1widecharacters)*字节数一列和范围一列可能根据程序编译和运行的系统不同而有所不同。这里列出的数值是多数32位系统的常用数据。对于其他系统,通常的说法是整型(int)具有根据系统结构建议的自然长度(即一个字oneword的长度),而4中整型数据char,short,int,long的长度必须是递增的,也就是说按顺序每一类型必须大于等于其前面一个类型的长度。同样的规则也适用于浮点数类型float,double和longdouble,也是按递增顺序。除以上列出的基本数据类型外,还有指针(pointer)和void参数表示类型,我们将在后面看到。变量的声明(Declarationofvariables)在C++中要使用一个变量必须先声明(declare)该变量的数据类型。声明一个新变量的语法是写出数据类型标识符(例如int,short,float...)后面跟一个有效的变量标识名称。例如:inta;floatmynumber;以上两个均为有效的变量声明(variabledeclaration)。第一个声明一个标识为a的整型变量(intvariable),第二个声明一个标识为mynumber的浮点型变量(floatvariable)。声明之后,我们就可以在后面的程序中使用变量a和mynumber了。如果你需要声明多个同一类型的变量,你可以将它们缩写在同一行声明中,在标识之间用逗号(comma)分隔。例如:inta,b,c;以上语句同时定义了a、b、c3个整型变量,它与下面的写法完全等同:inta;intb;intc;整型数据类型(char,short,long和int)可以是有符号的(signed)或无符号的(unsigned),这取决于我们需要表示的数据范围。有符号类型(signed)可以表示正177/177n数和负数,而无符号类型(unsigned)只能表示正数和0。在定义一个整型数据变量时可以在数据类型前面加关键字signed或unsigned来声明数据的符号类型。例如:unsignedshortNumberOfSons;signedintMyAccountBalance;如果我们没有特别写出signed或unsigned,变量默认为signed,因此以上第二个声明我们也可以写成:intMyAccountBalance;因为以上两种表示方式意义完全一样,因此我们在源程序通常省略关键字signed。唯一的例外是字符型(char)变量,这种变量独立存在,与signedchar和unsignedchar型均不相同。short和long可以被单独用来表示整型基本数据类型,short相当于shortint,long相当于longint。也就是说shortyear;和shortintyear;两种声明是等价的。最后,signed和unsigned也可以被单独用来表示简单类型,意思分别同signedint和unsignedint相同,即以下两种声明互相等同:unsignedMyBirthYear;unsignedintMyBirthYear;下面我们就用C++代码来解决在这一节开头提到的记忆问题,来看一下变量定义是如何在程序中起作用的。//operatingwithvariables#includeusingnamespacestd;intmain(){//declaringvariables:inta,b;intresult;177/177n//process:a=5;b=2;a=a+1;result=a-b;//printouttheresult:cout<177/177nusingnamespacestd;intmain(){inta=5;//初始值为5intb(2);//初始值为2intresult;//不确定初始值a=a+3;result=a-b;cout<,并且使用usingnamespace语句来使用标准名空间(std),如下面例子所示://C++字符串例题#include#includeusingnamespacestd;intmain(){177/177nstringmystring="Thisisastring";cout<#includeusingnamespacestd;intmain(){stringmystring;mystring="Thisistheinitialstringcontent";cout<usingnamespacestd;intmain(){inta,b;//a:?,b:?a=10;//a:10,b:?b=4;//a:10,b:4a=b;//a:4,b:4b=7;//a:4,b:7cout<<"a:";cout<>=,<<=,&=,^=,|=)177/177nC++以书写简练著称的一大特色就是这些组合运算符compoundassignationoperators(+=,-=,*=和/=及其他),这些运算符使得只用一个基本运算符就可改写变量的值:value+=increase;等同于value=value+increase;a-=5;等同于a=a-5;a/=b;等同于a=a/b;price*=units+1;等同于price=price*(units+1);其他运算符以此类推。例如://组合运算符例子#includeusingnamespacestd;intmain(){inta,b=3;a=b;a+=2;//相当于a=a+2cout<,<,>=,<=)我们用关系运算符来比较两个表达式。如ANSI-C++标准中指出的,关系预算的结果是一个bool值,根据运算结果的不同,它的值只能是真true或false。例如我们想通过比较两个表达式来看它们是否相等或一个值是否比另一个的值大。以下为C++的关系运算符:==相等Equal!=不等Different>大于Greaterthan177/177n<小于Lessthan>=大于等于Greaterorequalthan<=小于等于Lessorequalthan下面你可以看到一些实际的例子:(7==5)将返回false.(5>4)将返回true.(3!=2)将返回true.(6>=6)将返回true.(5<5)将返回false.当然,除了使用数字常量,我们也可以使用任何有效表达式,包括变量。假设有a=2,b=3和c=6,(a==5)将返回false.(a*b>=c)将返回true因为它实际是(2*3>=6)(b+4>a*c)将返回false因为它实际是(3+4>2*6)((b=2)==a)将返回true.注意:运算符=(单个等号)不同于运算符==(双等号)。第一个是赋值运算符(将等号右边的表达式值赋给左边的变量);第二个(==)是一个判断等于的关系运算符,用来判断运算符两边的表达式是否相等。因此在上面例子中最后一个表达式((b=2)==a),我们首先将数值2赋给变量b,然后把它和变量a进行比较。因为变量a中存储的也是数值2,所以整个运算的结果为true。在ANSI-C++标准出现之前的许多编译器中,就像C语言中,关系运算并不返回值为真true或假false的bool值,而是返回一个整型数值最为结果,它的数值可以为0,代表"false"或一个非0数值(通常为1)来代表"true"。177/177n逻辑运算符Logicoperators(!,&&,||)运算符!等同于boolean运算NOT(取非),它只有一个操作数(operand),写在它的右边。它做的唯一工作就是取该操作数的反面值,也就是说如果操作数值为真true,那么运算后值变为假false,如果操作数值为假false,则运算结果为真true。它就好像是说取与操作数相反的值。例如:!(5==5)返回false,因为它右边的表达式(5==5)为真true.!(6<=4)返回true因为(6<=4)为假false.!true返回假false.!false返回真true.逻辑运算符&&和||是用来计算两个表达式而获得一个结果值。它们分别对应逻辑运算中的与运算AND和或运算OR。它们的运算结果取决于两个操作数(operand)的关系:第一个操作数a第二个操作数b结果a&&b结果a||btruetruetruetruetruefalsefalsetruefalsetruefalsetruefalsefalsefalsefalse例如:((5==5)&&(3>6))返回false(true&&false).((5==5)||(3>6))返回true(true||false).条件运算符Conditionaloperator(?)条件运算符计算一个表达式的值并根据表达式的计算结果为真true或假false而返回不同值。它的格式是:177/177ncondition?result1:result2(条件?返回值1:返回值2)如果条件condition为真true,整个表达式将返回esult1,否则将返回result2。7==5?4:3返回3,因为7不等于5.7==5+2?4:3返回4,因为7等于5+2.5>3?a:b返回a,因为5大于3.a>b?a:b返回较大值,a或b.//条件运算符例子#includeusingnamespacestd;intmain(){inta,b,c;a=2;b=7;c=(a>b)?a:b;cout<b)运算值为假(false),所以整个表达式(a>b)?a:b要取分号后面的值,也就是b的值7。因此最后输出c的值为7。逗号运算符(,)177/177n逗号运算符(,)用来分开多个表达式,并只取最右边的表达式的值返回。例如有以下代码:a=(b=3,b+2);这行代码首先将3赋值给变量b,然后将b+2赋值给变量a。所以最后变量a的值为5,而变量b的值为3。位运算符BitwiseOperators(&,|,^,~,<<,>>)位运算符以比特位改写变量存储的数值,也就是改写变量值的二进制表示:opasmDescription&AND逻辑与LogicAND|OR逻辑或LogicOR^XOR逻辑异或LogicalexclusiveOR~NOT对1取补(位反转)Complementtoone(bitinversion)<>SHR右移ShiftRight变量类型转换运算符Explicittypecastingoperators变量类型转换运算符可以将一种类型的数据转换为另一种类型的数据。在写C++中有几种方法可以实现这种操作,最常用的一种,也是与C兼容的一种,是在原转换的表达式前面加用括号()括起的新数据类型:inti;floatf=3.14;i=(int)f;以上代码将浮点型数字3.14转换成一个整数值(3)。这里类型转换操作符为(int)。在C++中实现这一操作的另一种方法是使用构造函数constructor的形式:在要转换的表达式前加变量类型并将表达式括在括号中:177/177ni=int(f);以上两种类型转换的方法在C++中都是合法的。另外ANSI-C++针对面向对象编程(objectorientedprogramming)增加了新的类型转换操作符(参考Section5.4,Advancedclasstype-casting).sizeof()这个运算符接受一个输入参数,该参数可以是一个变量类型或一个变量自己,返回该变量类型(variabletype)或对象(object)所占的字节数:a=sizeof(char);这将会返回1给a,因为char是一个常为1个字节的变量类型。sizeof返回的值是一个常数,因此它总是在程序执行前就被固定了。其它运算符在本教程后面的章节里我们将看到更多的运算符,比如指向指针的运算或面向对象编程特有的运算,等等,我们会在它们各自的章节里进行详细讨论。运算符的优先度Precedenceofoperators当多个操作数组成复杂的表达式时,我们可能会疑惑哪个运算先被计算,哪个后被计算。例如以下表达式:a=5+7%2我们可以怀疑它实际上表示:a=5+(7%2)结果为6,还是a=(5+7)%2结果为0?正确答案为第一个,结果为6。每一个运算符有一个固定的优先级,不仅对数学运算符(我们可能在学习数学的时候已经很了解它们的优先顺序了),所有在C++中出现的运算符都有优先级。从最从最高级到最低级,运算的优先级按下表排列:优先级Level操作符Operator说明177/177nDescription结合方向Grouping1::范围从左到右2()[].->++--dynamic_caststatic_castreinterpret_castconst_casttypeid后缀从左到右3++--~!sizeofnewdelete一元(前缀)从右到左*&指针和取地址+-一元符号4(type)类型转换从右到左5.*->*指向成员的指针从左到右6*/%乘、除、取模从左到右7+-加减从左到右8<<>>位移从左到右9<><=>=关系操作符从左到右10==!=等于、不等于从左到右11&按位与运算从左到右12^按位异或运算从左到右13|按位或运算从左到右14&&逻辑与运算从左到右15||逻辑或运算从左到右16?:条件运算从右到左17=*=/=%=+=-=>>=<<=&=^=|=赋值运算从右到左18,逗号从左到右177/177n结合方向Grouping定义了当有同优先级的多个运算符在一起时,哪一个必须被首先运算,最右边的还是最左边的。所有这些运算符的优先级顺序可以通过使用括号parenthesissigns(和)来控制,而且更易读懂,例如以下例子:a=5+7%2;根据我们想要实现的计算的不同,可以写成:a=5+(7%2);或者a=(5+7)%2;所以如果你想写一个复杂的表达式而不敢肯定各个运算的执行顺序,那么就加上括号。这样还可以使代码更易读懂。1.5控制台交互(Communicationthroughconsole)控制台(console)是电脑的最基本交互接口,通常包括键盘(keyboard)和屏幕(screen)。键盘通常为标准输入设备,而屏幕为标准输出设备。在C++的iostream函数库中,一个程序的标准输入输出操作依靠两种数据流:cin给输入使用和cout给输出使用。另外,cerr和clog也已经被实现――它们是两种特殊设计的数据流专门用来显示出错信息。它们可以被重新定向到标准输出设备或到一个日志文件(logfile)。因此cout(标准输出流)通常被定向到屏幕,而cin(标准输入流)通常被定向到键盘。通过控制这两种数据流,你可以在程序中与用户交互,因为你可以在屏幕上显示输出并从键盘接收用户的输入。输出Output(cout)输出流cout与重载(overloaded)运算符<<一起使用:cout<<"Outputsentence";//打印Outputsentence到屏幕上cout<<120;//打印数字120到屏幕上cout<>)来实现的。它后面必须跟一个变量以便存储读入的数据。例如:intage;cin>>age;声明一个整型变量age然后等待用户从键盘输入到cin并将输入值存储在这个变量中。cin只能在键盘输入回车键(RETURN)后才能处理前面输入的内容。因此即使你只要求输入一个单独的字符,在用户按下回车键(RETURN)之前cin将不会处理用户的输入的字符。在使用cin输入的时候必须考虑后面的变量类型。如果你要求输入一个整数,extraction(>>)后面必须跟一个整型变量,如果要求一个字符,后面必须跟一个字符型变量,如果要求一个字符串,后面必须跟一个字符串型变量。//i/oexample#includeintmain(){inti;cout<<"Pleaseenteranintegervalue:";cin>>i;cout<<"Thevalueyouenteredis"<>a>>b;等同于:cin>>a;cin>>b;在以上两种情况下用户都必须输入两个数据,一个给变量a,一个给变量b。输入时两个变量之间可以以任何有效的空白符号间隔,包括空格,跳跃符tab或换行。cin和字符串我们可以像读取基本类型数据一样,使用cin和>>操作符来读取字符串,例如:cin>>mystring;但是,cin>>只能读取一个单词,一旦碰到任何空格,读取操作就会停止。在很多时候这并不是我们想要的操作,比如我们希望用户输入一个英文句子,那么这种方法就无法读取完整的句子,因为一定会遇到空格。要一次读取一整行输入,需要使用C++的函数getline,相对于是用cin,我们更建议使用getline来读取用户输入。例如://读取字符串例子#include#include177/177nusingnamespacestd;intmain(){stringmystr;cout<<"What'syourname?";getline(cin,mystr);cout<<"Hello"<定义了一个叫做 stringstream的类,使用这个类可以对基于字符串的对象进行像流(stream)一样的操作。这样,我们可以对字符串进行抽取和插入操作,这对将字符串与数值互相转换非常有用。例如,如果我们想将一个字符串转换为一个整数,可以这样写:stringmystr("1204");intmyint;stringstream(mystr)>>myint;这个例子中先定义了一个字符串类型的对象mystr,初始值为"1204",又定义了一个整数变量myint。然后我们使用stringstream类的构造函数定义了这个类的对象,并以字符串变量mystr为参数。因为我们可以像使用流一样使用stringstream的对象,所177/177n以我们可以像使用cin那样使用操作符>>后面跟一个整数变量来进行提取整数数据。这段代码执行之后变量myint存储的是数值1204。//字符串流的使用示例#include#include#includeusingnamespacestd;intmain(){stringmystr;floatprice=0;intquantity=0;cout<<"Enterprice:";getline(cin,mystr);stringstream(mystr)>>price;cout<<"Enterquantity:";getline(cin,mystr);stringstream(mystr)>>quantity;cout<<"Totalprice:"<0)cout<<"xispositive";elseif(x<0)177/177ncout<<"xisnegative";elsecout<<"xis0";记住当我们需要执行多条语句时,必须使用花括号{}将它们括起来以组成一个语句块blockofinstructions。重复结构Iterationstructures或循环loops循环Loops的目的是重复执行一组语句一定的次数或直到满足某种条件。while循环格式是:while(表达式expression)语句statement它的功能是当expression的值为真true时重复执行statement。例如,下面我们将用while循环来写一个倒计数程序://customcountdownusingwhile#includeintmain(){intn;cout<<"Enterthestartingnumber>";cin>>n;while(n>0){cout<88,7,6,5,4,3,2,1,FIRE!177/177n程序开始时提示用户输入一个倒计数的初始值。然后while循环开始,如果用户输入的数值满足条件n>0(即n比0大),后面跟的语句块将会被执行一定的次数,直到条件(n>0)不再满足(变为false)。以上程序的所有处理过程可以用以下的描述来解释:从main开始:1.用户输入一个数值赋给n.2.while语句检查(n>0)是否成立,这时有两种可能:otrue:执行statement(到第3步)ofalse:跳过statement.程序直接执行第5步.3.执行statement:cout<intmain(){unsignedlongn;do{cout<<"Enternumber(0toend):";cin>>n;cout<<"Youentered:"<intmain(){for(intn=10;n>0;n--){cout<intmain(){intn;for(n=10;n>0;n--){cout<intmain(){177/177nfor(intn=10;n>0;n--){if(n==5)continue;cout<intmain(){intn=10;loop:cout<0)gotoloop;cout<<"FIRE!";return0;}10,9,8,7,6,5,4,3,2,1,FIRE!exit函数exit是一个在cstdlib(stdlib.h)库中定义的函数。exit的目的是一个特定的退出代码来结束程序的运行,它的原型(prototype)是:177/177nvoidexit(intexitcode);exitcode是由操作系统使用或被调用程序使用。通常exitcode为0表示程序正常结束,任何其他值表示程序执行过程中出现了错误。选择结构TheselectiveStructure:switchswitch语句的语法比较特殊。它的目标是对一个表达式检查多个可能常量值,有些像我们在本节开头学习的把几个if和elseif语句连接起来的结构。它的形式是:switch(expression){caseconstant1:blockofinstructions1break;caseconstant2:blockofinstructions2break;...default:defaultblockofinstructions}它按以下方式执行:switch计算表达式expression的值,并检查它是否与第一个常量constant1相等,如果相等,程序执行常量1后面的语句块blockofinstructions1直到碰到关键字break,程序跳转到switch选择结构的结尾处。如果expression不等于constant1,程序检查表达式expression的值是否等于第二个常量constant2,如果相等,程序将执行常量2后面的语句块blockofinstructions2直到碰到关键字break。依此类推,直到最后如果表达式expression的值不等于任何前面的常量(你可以用case语句指明任意数量的常量值来要求检查),程序将执行默认区default:后面的语句,如果它存在的话。default:选项是可以省略的。177/177n下面的两段代码段功能相同:switchexampleif-elseequivalentswitch(x){case1:cout<<"xis1";break;case2:cout<<"xis2";break;default:cout<<"valueofxunknown";}if(x==1){cout<<"xis1";}elseif(x==2){cout<<"xis2";}else{cout<<"valueofxunknown";}前面已经提到switch的语法有点特殊。注意每个语句块结尾包含的break语句。这是必须的,因为如果不这样做,例如在语句块blockofinstructions1的结尾没有break,程序执行将不会跳转到switch选择的结尾处(}),而是继续执行下面的语句块,直到第一次遇到break语句或到switch选择结构的结尾。因此,不需要在每一个case域内加花括号{}。这个特点同时可以帮助实现对不同的可能值执行相同的语句块。例如:switch(x){case1:case2:case3:cout<<"xis1,2or3";177/177nbreak;default:cout<<"xisnot1,2nor3";}注意switch只能被用来比较表达式和不同常量的值constants。因此我们不能够把变量或范围放在case之后,例如(case(n*2):)或(case(1..3):)都不可以,因为它们不是有效的常量。如果你需要检查范围或非常量数值,使用连续的if和elseif语句。2.2函数I(FunctionsI)通过使用函数(functions)我们可以把我们的程序以更模块化的形式组织起来,从而利用C++所能提供的所有结构化编程的潜力。一个函数(function)是一个可以从程序其它地方调用执行的语句块。以下是它的格式:typename(argument1,argument2,...)statement这里:?type是函数返回的数据的类型?name是函数被调用时使用的名?argument是函数调用需要传入的参量(可以声明任意多个参量)。每个参量(argument)由一个数据类型后面跟一个标识名称组成,就像变量声明中一样(例如,intx)。参量仅在函数范围内有效,可以和函数中的其它变量一样使用,它们使得函数在被调用时可以传入参数,不同的参数用逗号(comma)隔开.?statement是函数的内容。它可以是一句指令,也可以是一组指令组成的语句块。如果是一组指令,则语句块必须用花括号{}括起来,这也是我们最常见到情况。其实为了使程序的格式更加统一清晰,建议在仅有一条指令的时候也使用花括号,这是一个良好的编程习惯。下面看一下第一个函数的例子://functionexample#include177/177nintaddition(inta,intb){intr;r=a+b;return(r);}intmain(){intz;z=addition(5,3);cout<<"Theresultis"<intsubtraction(inta,intb){intr;r=a-b;return(r);}intmain()177/177n{intx=5,y=3,z;z=subtraction(7,2);cout<<"Thefirstresultis"<usingnamespacestd;177/177nvoidprintmessage(){cout<<"I'mafunction!";}intmain(){printmessage();return0;}I'mafunction!void还可以被用在函数参数位置,表示我们明确希望这个函数在被调用时不需要任何参数。例如上面的函数printmessage也可以写为以下形式:voidprintmessage(void){cout<<"I'mafunction!";}虽然在C++中void可以被省略,我们还是建议写出void,以便明确指出函数不需要参数。你必须时刻知道的是调用一个函数时要写出它的名字并把参数写在后面的括号内。但如果函数不需要参数,后面的括号并不能省略。因此调用函数printmessage的格式是printmessage();函数名称后面的括号就明确表示了它是一个函数调用,而不是一个变量名称或其它什么语句。以下调用函数的方式就不对:printmessage;2.3函数II(FunctionsII)参数按数值传递和按地址传递(Argumentspassedbyvalueandbyreference)177/177n到目前为止,我们看到的所有函数中,传递到函数中的参数全部是按数值传递的(byvalue)。也就是说,当我们调用一个带有参数的函数时,我们传递到函数中的是变量的数值而不是变量本身。例如,假设我们用下面的代码调用我们的第一个函数addition:intx=5,y=3,z;z=addition(x,y);在这个例子里我们调用函数addition同时将x和y的值传给它,即分别为5和3,而不是两个变量:这样,当函数addition被调用时,它的变量a和b的值分别变为5和3,但在函数addition内对变量a或b所做的任何修改不会影响变量他外面的变量x和y的值,因为变量x和y并没有把它们自己传递给函数,而只是传递了他们的数值。但在某些情况下你可能需要在一个函数内控制一个函数以外的变量。要实现这种操作,我们必须使用按地址传递的参数(argumentspassedbyreference),就象下面例子中的函数duplicate://passingparametersbyreference#includevoidduplicate(int&a,int&b,int&c){a*=2;b*=2;c*=2;}intmain(){intx=1,y=3,z=7;duplicate(x,y,z);cout<<"x="<。这就是为什么上面的程序中,主程序main中的三个变量x,y和z在调用函数duplicate后打印结果显示他们的值增加了一倍。如果在声明下面的函数:voidduplicate(int&a,int&b,int&c)时,我们是按这样声明的:voidduplicate(inta,intb,intc)也就是不写地址符ampersand(&),我们也就没有将参数的地址传递给函数,而是传递了它们的值,因此,屏幕上显示的输出结果x,y,z的值将不会改变,仍是1,3,7。这种用地址符ampersand(&)来声明按地址"byreference"传递参数的方式只是在C++中适用。在C语言中,我们必须用指针(pointers)来做相同的操作。按地址传递(Passingbyreference)是一个使函数返回多个值的有效方法。例如,下面是一个函数,它可以返回第一个输入参数的前一个和后一个数值。//morethanonereturningvalue#includevoidprevnext(intx,int&prev,int&next){prev=x-1;next=x+1;177/177n}intmain(){intx=100,y,z;prevnext(x,y,z);cout<<"Previous="<intdivide(inta,intb=2){intr;r=a/b;return(r);}intmain(){cout<intdivide(inta,intb){return(a/b);}floatdivide(floata,floatb){return(a/b);}intmain(){177/177nintx=5,y=2;floatn=5.0,m=2.0;cout<longfactorial(longa){if(a>1)return(a*factorial(a-1));elsereturn(1);}intmain(){longl;cout<<"Typeanumber:";cin>>l;cout<<"!"<voidodd(inta);voideven(inta);intmain(){inti;do{cout<<"Typeanumber:(0toexit)";177/177ncin>>i;odd(i);}while(i!=0);return0;}voidodd(inta){if((a%2)!=0)cout<<"Numberisodd.n";elseeven(a);}voideven(inta){if((a%2)==0)cout<<"Numberiseven.n";elseodd(a);}Typeanumber(0toexit):9Numberisodd.Typeanumber(0toexit):6Numberiseven.Typeanumber(0toexit):1030Numberiseven.Typeanumber(0toexit):0Numberiseven.这个例子的确不是很有效率,我相信现在你已经可以只用一半行数的代码来完成同样的功能。但这个例子显示了函数原型(prototypingfunctions)是怎样工作的。并且在这个具体的例子中,两个函数中至少有一个是必须定义原型的。这里我们首先看到的是函数odd和even的原型:voidodd(inta);voideven(inta);这样使得这两个函数可以在它们被完整定义之前就被使用,例如在main中被调用,这样main就可以被放在逻辑上更合理的位置:即程序代码的开头部分。177/177n尽管如此,这个程序需要至少一个函数原型定义的特殊原因是因为在odd函数里需要调用even函数,而在even函数里也同样需要调用odd函数。如果两个函数任何一个都没被提前定义原型的话,就会出现编译错误,因为或者odd在even函数中是不可见的(因为它还没有被定义),或者even函数在odd函数中是不可见的。很多程序员建议给所有的函数定义原型。这也是我的建议,特别是在有很多函数或函数很长的情况下。把所有函数的原型定义放在一个地方,可以使我们在决定怎样调用这些函数的时候轻松一些,同时也有助于生成头文件。第三章高级数据类型(AdvancedData)1.数组Arrays2.字符序列CharactersSequences3.指针Pointers4.动态内存分配Dynamicmemory5.数据结构DataStructures6.自定义数据类型Userdefineddatatypes3.1数组(Arrays)数组(Arrays)是在内存中连续存储的一组同种数据类型的元素(变量),每一数组有一个唯一名称,通过在名称后面加索引(index)的方式可以引用它的每一个元素。177/177n也就是说,例如我们有5个整型数值需要存储,但我们不需要定义5个不同的变量名称,而是用一个数组(array)来存储这5个不同的数值。注意数组中的元素必须是同一数据类型的,在这个例子中为整型(int)。例如一个存储5个整数叫做billy的数组可以用下图来表示:这里每一个空白框代表数组的一个元素,在这个例子中为一个整数值。白框上面的数字0到4代表元素的索引(index)。注意无论数组的长度如何,它的第一个元素的索引总是从0开始的。同其它的变量一样,数组必须先被声明然后才能被使用。一种典型的数组声明显示如下:typename[elements];这里type是可以使任何一种有效的对象数据类型(objecttype),如int,float...等,name是一个有效地变量标识(identifier),而由中括号[]引起来的elements域指明数组的大小,即可以存储多少个元素。因此,要定义上面图中显示的billy数组,用一下语句就可以了:intbilly[5];备注:在定义一个数组的时候,中括号[]中的elements域必须是一个常量数值,因为数组是内存中一块有固定大小的静态空间,编译器必须在编译所有相关指令之前先能够确定要给该数组分配多少内存空间。初始化数组(Initializingarrays)当声明一个本地范围内(在一个函数内)的数组时,除非我们特别指定,否则数组将不会被初始化,因此它的内容在我们将数值存储进去之前是不定的。如果我们声明一个全局数组(在所有函数之外),则它的内容将被初始化为所有元素均为0。因此,如果全局范围内我们声明:intbilly[5];那么billy中的每一个元素将会被初始化为0:177/177n另外,我们还可以在声明一个变量的同时把初始值付给数组中的每一个元素,这个赋值用花括号{}来完成。例如:intbilly[5]={16,2,77,40,12071};这个声明将生成如下数组:花括号中我们要初始化的元素数值个数必须和数组声明时方括号[]中指定的数组长度相符。例如,在上面例子中数组billy声明中的长度为5,因此在后面花括号中的初始值也有5个,每个元素一个数值。因为这是一种信息的重复,因此C++允许在这种情况下数组[]中为空白,而数组的长度将有后面花括号{}中数值的个数来决定,如下例所示。intbilly[]={16,2,77,40,12071};存取数组中数值(AccesstothevaluesofanArray)在程序中我们可以读取和修改数组任一元素的数值,就像操作其他普通变量一样。格式如下:name[index]继续上面的例子,数组billy有5个元素,其中每一元素都是整型int,我们引用其中每一个元素的名字分别为如下所示:例如,要把数值75存入数组billy中第3个元素的语句可以是:billy[2]=75;又例如,要把数组billy中第3个元素的值赋给变量a,我们可以这样写:a=billy[2];因此,在所有使用中,表达式billy[2]就像任何其他整型变量一样。注意数组billy的第3个元素为billy[2],因为索引(index)从0开始,第1个元素是billy[0],第2个元素是billy[1],因此第3个是billy[2]。同样的原因,最后一个元素是billy[4]。如果我们写billy[5],那么是在使用billy的第6个元素,因此会超出数组的长度。177/177n在C++中对数组使用超出范围的index是合法的,这就会产生问题,因为它不会产生编译错误而不易被察觉,但是在运行时会产生意想不到的结果,甚至导致严重运行错误。超出范围的index之所以合法的原因我们在后面学习指针(pointer)的时候会了解。学到这里,我们必须能够清楚的了解方括号[]在对数组操作中的两种不同用法。它们完成两种任务:一种是在声明数组的时候定义数组的长度;另一种是在引用具体的数组元素的时候指明一个索引号(index)。我们要注意不要把这两种用法混淆。intbilly[5];//声明新数组(以数据类型名称开头)billy[2]=75;//存储数组的一个元素其它合法的数组操作:billy[0]=a;//a为一个整型变量billy[a]=75;b=billy[a+2];billy[billy[a]]=billy[2]+5;//arraysexample#includeintbilly[]={16,2,77,40,12071};intn,result=0;intmain(){for(n=0;n<5;n++){result+=billy[n];}cout<#defineWIDTH5#defineHEIGHT3intjimmy[HEIGHT][WIDTH];177/177nintn,m;intmain(){for(n=0;n#defineWIDTH5#defineHEIGHT3intjimmy[HEIGHT*WIDTH];intn,m;intmain(){for(n=0;nvoidprintarray(intarg[],intlength){for(intn=0;n