C++Primer笔记.pdf
C+P ri m e r 笔记新一篇:VC笔记第 1 章 开 始1 C+程序程序源文件的名字,一般包括两部分:文件名以及文件后缀。文件后缀一般用来标识文件的内容头文件:一般以.h 后缀结尾程序文本文件:C+一般以.c p p 后缀结尾C+标准库中的名字都是在一个称作s t d 的名字空间中声明的,这些名字在我们的程序文本文件中是不可见的。除非我们显式地使它们可见u si n g 指示符告诉编译器要使用在名字空间s t d 中声明的名字2、预处理器指示符预 处 理 器 指 示 符 用 号 标 识,这个符号将放在程序中该行的最起始一列上。处理这些指示符的程序被称做预处理器,通常捆绑在编译器中#i n c l u d e#i n c l u d e 指示符读入指定文件的内容,它有两种格式#i n c l u d e#i n c l u d e h 如果文件名用类括号 和括起来表明这个文件是一个工程或标准头文件,查找过程会检查预定义的目录。我们可以通过设置搜索路径环境变量或命令行选项来修改这些目录如果文件名用一对引号括起来,则表明该文件是用户提供的头文件,查找该文件时将从当前文件目录开始Si f n d e f由于嵌套包含文件的原因,一个头文件可能会被多次包含在一个源文件中.条件指示符可防止这种头文件的重复处理#i f n d e f B O O K STO RE _ HSd e f i n e B O O K STO RE _ H/*B o o k st o re,h 的内容*/#e n d i f条件指示符#i f n d e f 检查B O O K STO RE !在前面是否已经被定义,这里B O O K STO RE _ H 是一个预编译器常量,习惯上预编译器常量往往被写成大写字母。如果B O O K STO RE!在前面没有被定义,则条件指示符的值为真。于是从#i f n d e f到#e n d i f 之间而所有语句都被包含进来进行处理,相反如果#i f n d e f 指示符的值为假则它与#e n d i f 指示符之间的行将被忽略为了保证头文件只被处理一次,把如下#d e f i n e 指示符#d e f i n e B O O K STO RE _ H放在#i f n d e f 后面,这样在头文件的内容第一次被处理时一,B O O K STO RE _ H 将被定义,从而防止了在程序文本文件中以后#i f n d e f 指示符的值为真Si f d e f#if d e f 指示符常被用来判断一个预处理器常量是否已被定义,以便有条件地包含程序代码我们在编译程序时可以使用-D 选项,并且在后面写上预处理器常量的名字,这样就能在命令行中定义预处理器常量$CC-DDEBUG main.C也可以在程序中用#d e fin e 指示符定义预处理器常量3、注释注释是用来帮助程序员读程序的语言结构,它是一种程序礼仪,可以用来概括程序的算法、标识变量的意义、或者阐明一段比较难懂的程序代码。注释不会增加程序的可执行代码的长度。在代码生成以前,编译器会将注释从程序中剔除掉C+中有两种注释符号:一种是注释对/*/,与 C 语言中的一样。注释的开始用/*标记,编译器会把/*与*/之间的代码当作注释。第二种注释符是双斜线,它可用来注释一个单行,程序行中注释符右边的内容都将被当作注释而被编译器忽略第 2 章 C+浏览1、内置数组数据类型C+为基本算术数据类型提供了内置的支持另外,标准库还支持基本类抽象的组合在内置数据类型与标准类库之间是复合类型,特别是指针和数组。数组a r r a y 是一种顺序容器它包含单一类型的元素2、动态内存分配和指针在 C+中对象可以静态分配即编译器在处理程序源代码时分配,也可以动态分配一一即程序执行时一,调用运行时刻库函数来分配。这两种内存分配方法的主要区别是效率与灵活性之间的平衡准则不同。出于静态内存分配是在程序执行之前进行的,因而效率比较高。但是它缺少灵活性,它要求在程序执行之前就知道所需内存的类型和数量。一般来说,存储未知数目的元素需要动态内存分配的灵活性C+支持用指针类型来存放对象的内存地址值。C+预定义了 一个专门的取地址操作符&,当我们把它应用在一个对象上时,返回的是对象的地址值。为了访问指针所指向的实际对象,我们必须先用解引用操作符*来解除指针的引用在 C+中指针的主要用处是管理和操纵动态分配的内存静态与动态内存分配的两个主要区别是:1.静态对象是有名字的变量,我们直接对其进行操作。而动态对象是没有名字的变量,我们通过指针间接地对它进行操作2.静态对象的分配与释放由编译器自动处理,程序员需要理解这一点,但不需要做任何事情。相反动态对象的分配与释放,必须由程序员显式地管理,相对来说比较容易出错,它通 过 new 和 d e le te 两个表达式来完成d e le te 表达式的两个版本:删除单个对象d e le te 指针;删除对象数组d e l e t e d 指针;如果忘了删除动态分配的内存,程序就会在结束时出现内存泄露问题。内存泄露是指一块动态分配的内存,我们不再拥有指向这块内存的指针,因此我们没有办法将它返还给程序供以后重新使用3、基于对象的设计支持基于对象设计的类的一般形式如下c l a s s c l a s s n a m e pu b l i c:/公有操作集合pr i v a t e:/私有实现代码);类定义包括两个部分:类头,由关键字c l a s s与相关联的类名构成。类体由花括号括起来,以分号结束。类头本身也用作类的声明。通过使用两个成员访问操作符中的一个,我们可以调用一个有名字的成员函数。这两个操作符为用于类对象的点操作符.以及用于类对象指针的箭头操作符-在类定义中被定义的成员函数会被自动当作是内联函数,此外我们也可以用i n l i n e关键字显式地要求一个函数被视为内联函数。构造函数程序设计中的一个常见错误是使用事先并没向被正确初始化的对象。实际上,这是一个极为常见的错误。所 以C+为用户定义的类提供了一种自动初始化机制:类构造函数,构造函数是一种特殊的类成员函数,专门用于初始化对象。如果构造函数被定义了,那么在类的每个对象第一次被使用之前,这构造函数就被自动应用在对象上函数重载C+支持被称为函数重载的机制,函数重载允许两个或更多个函数使用同一个名字。限制条件是它们的参数表必须不同:参数类型不同或参数的数目不同。根据不同的参数表编译器就能够判断出对某个特定的调用应该选择哪一个版本的重载函数重载函数在运行时刻的行为与非重载函数完全一样,主要的负担是在编译时刻用来决定中该调用哪个实例所需要的时间在类体的外面定义类的成员函数,惟一的语法区别是要指出成员函数属于哪个类,这可以通过类域操作符来实现,类名:析构函数每个类对象在被程序最后一次使用之后,它的析构函数就会被自动调用。我们通过在类的名字前面加一个波浪线】来标识析构函数。一般地,析构函数会释放在类对象使用和构造过程中所获得的资源类定义以及相关的常数值或t y pe d e f名通常存储在头文件中,并且头文件以类名来命名。不在类定义内部定义的类成员函数都存储在与类名同名的程序文本文件中。这些函数不用随每个使用相关类的程序而重新编译,这些成员函数通过预编译之后被保存在类库中4、面向对象程序设计子类型与基类共享公共的接口公有操作的公共集。由于共享公共接口,允许了子类和基类在程序内部可互换使用,而无需考虑对象的实际类型。从某种意义上来说公共接口封装了单个子类型中与类型相关的细节,类之间的类型/子类型关系形成了继承或派生层次关系虚拟函数为了把一个类设计成基类,设计考虑是找出类型相关的成员函数,并把这些成员函数标记为v i rtua l。对于类型相关的成员函数,它的算法由特定的基类或派生类的行为或实现来决定对于一个非虚拟函数的调用,编译器在编译时刻选择被调用的函数,而非虚拟函数的决定则要等到运行时刻。在执行程序内部的每一个调用点上,系统根据被调用对象的实际基类或派生类的类型来决定选择哪一个虚拟函数实例派生类对象的初始化过程派生类对象实际上由几部分构成:每个基类是一个类的子对象,它在新定义的派生类中有独立的一部分。派生类对象的初始化过程是这样的,首先自动调用每个基类的构造函数来初始化相关的基类子对象,然后再执行派生类的构造函数。从设计的角度来看,派生类的构造函数应该只初始化那些在派生类中被定义的数据成员,而不是基类中的数据成员因为基类的构造函数并没有被派生类继承(析构函数和拷贝赋值操作符同样也没有),我们需要派生类的构造函数作为向基类构造函数传递参数的接口C+支持另外两种形式的继承:多继承,也译多重继承,也就是一个类可以从两个或多个基类派生而来。以及虚拟继承,在这种继承方式下,基类的单个实例在多个派生类之间共享5、泛 型 设 计(函数模板和类模板)C+的模板设施提供了一种机制,它能够将类或函数定义内部的类型和值参数化,这些参数在其他方面不变的代码中用作占位符,以后,这些参数会被绑定到实际类型上,可能是内置的类型,也可能是用户定义的类型关键字te m pl a te引入模板,模板参数由一对尖括号 括起来,类型标识符由关键字c l a ss标明,类型标识符作为实际类型的占位符当我们为类模板定义对象时一,编译器必须为相关的对象分配内存。为了做到这一点,形式模板参数被绑定到指定的实际参数类型上。不是所有类模板的成员函数都能自动地随类模板的实例化而被实例化,只有真正被程序使用到的成员函数才会被实例化。模板机制也支持面向对象的程序设计,类模板可以作为基类或派生类6、基于异常的设计异常是指在运行时刻程序出现的反常情形,例如数组下标越界、打开文件失败以及动态可用内存耗尽异常处理为“响应运行时刻的程序异常”提供了一个标准的语言级设施,它支持统一的语法和风格,也允许每个程序员进行微调异常处理机制的主要构成:1.程序中异常出现的点。一旦识别出程序异常,就会导致抛出(rai s e或t hro w)异常。异常被抛出时,正常的程序就被刮起,直到异常被处理完毕2.程序中异常被处理的点。程序异常的抛出与处理位于独立的函数或成员函数调用中。找到处理代码通常要涉及到展开程序调用栈。一旦异常被处理完毕,就恢复正常的程序执行。但不是在发生异常的地方恢复执行过程,而是在处理异常的地方恢复执行过程如果异常机制按照函数被调用的顺序回查每个函数直道m ai n。函数,仍然没有找到处理代码,那么它将调用标准库函数t erm i n at e。缺省情况下,t erm i n at e0函数结束程序一种特殊的、能够处理全部异常的cat ch子句如下:cat ch(.)(处理所有异常;虽然它无法访问异常对象)7、名字空间名字空间机制允许我们封装名字,否则这些名字就有可能会污染全局名字空间。一般来说,只有当我们希望自己的代码被外部软件开发部门使用时.,才使用名字空间关键字n am es p ace后面的名字标识了一个名字空间,它独立于全局名字空间,我们可以在里面放一些希望声明在函数或类体之外的实体为了提供有意义的名字空间,同时程序员又能很方便地访问在名字空间内定义的实体,C+提供了别名设施。名字空间别名允许用一个可替代的、短的或更一般的名字与一个现有的名字空间关联起来/提供一个更一般化的别名n am as p ace L I B =I B M _C an ad a_L abo rat o ry;u s i n g指示符使名字空间内的云有声明都可见,这样这些声明能够不加限定地使用usi ng na me spa c e I B M _C a na d a _Li b ora tory;usi ng声明提供了选择更为精如的名字可视性机制,它允许使名字空间中的单个声明可见usi ng I B M _C a na d a _Li b ora tory:M a tri x;为了防止标准C+用的组件污染用户程序的全局名字空间,所有标准C+库的组件都被声明在一个被称为std的名字空间内8、标准数组向量在标准C+中,数组类是C+标准库的一部分,现在它不叫数组,而叫向量了向量是一个类模板#i nc lud e v e c tor v e c O;空的 v e c torv e c tor v e c l(10);标 准v e c tor类模板支持使用下标操作符,另一种遍历方法是使用迭代器对来标记向量的开始处和结束处。迭代器是一个支持指针类型对象的类对象。V e c tor类模板提供了一对操作b e g i n。和e nd(),它们分别返回指向“向量开始处”和“结束处后一个”的迭代器,这一对迭代器合起来可以标记出待遍历元素的范围v e c tor:i te ra tor i te r=v e c.b e g i n();i te r+;*i te r=3;i te ra tor是v e c tor类模板中用ty pe d e f定义的类型能够应用到向量上的操作惊人地多,但是它们并不是作为v e v tor类模板的成员函数提供的。它们是以一个独立的泛型算法集的形式,由标准库提供。泛型算法接受一对迭代器,它们标记了要遍历的元素的范围,泛型算法还能接受指向内置数组的指针对标准库还提供了对ma p关联数组的支持,即数组元素可以被整数之外的其他东西索引第3章C+数据类型我们编写的程序以及所保存的程序数据在计算机的内存中是以二进制位序列的方式存放的。位是含有0或1值的一个单元,在物理上它的值是个负或正电荷通过对内存进行组织,我们可以引用特定的位集合,用地址引用内存类型抽象使我们能够对一个定长的位序列进行有意义的解释1、文字常量C+预定义了一组数值数据类型(内置数据类型),可以用来表示整数、浮点数和单个字符。此外,还定义了用来表示字符串的字符数组当一个数值,出现在程序中时,它被称为文字常量:称 之 为“文字”是因为我们只能以它的值的形式指代它,称 之 为“常量”是因为它的值不能被改变。每个文字都有相应的类型。文字常量是不可寻址的,尽管它的值也存储在机器内存的某个地方,但是我们没有办法访问它的地址整数文字常量可以被写成十进制、八进制或者十六进制的形式(这不会改变该整数值得位序列)在缺省情况下,整形文字常量被当作是一个i n t型的有符号值。我们可以在文字常量后面加一个“L”将其指定为l o n g类型。类似地,我们可以在整数文字常量的后面加上“U”,将其指定为一个无符号数。此外,我们还可以指定无符号l o n g型的文字常量浮点型文字常量可以被写成科学计数法或普通的十进制形式。浮点型文字常量在缺省情况下被认为是do ub l e型,单精度文字常量由值后面的“F”来标示。类似地,扩展精度中值后面跟的“L”表示单 词t ur e和f a l s e是b o o l型的文字常量可打印的文字字符常量可以写成用单引号括起来的形式,一部分不可打印的字符、单引号、双引号以及反斜杠用转义序列来表示字符串文字常量由零个或多个用双引号括起来的字符组成。不可打印字符可以由相应的转义序列来表示。字符串文字常量的类型是常量字符数组。它由字符串文字本身以及编译器加上的表示结束的空字符构成。空字符是C和C+用来标记字符串结束的符号2、变量变量为我们提供了一个有名字的内存存储区,可以通过程序对其进行读、写和处理。C+中的每个符号变量都与一个特定的数据类型相关联,这个类型决定了相关内存的大小、布局、能够存储在该内存区的值的范围以及可以应用其上的操作集变量和文字常量都有存储区,并且有相关的类型。区别在于变量是可寻址的,对于每一个变量,都有两个值与其相关联:1 .它的数据值,存储在某个内存地址中2 .它的地址值一一即,存储数据值的那块内存的地址变量的定义会引起相关内存的分配,因为一个对象只能有一个位置,所以程序中的每个对象只能被定义一次在C+中,程序在使用对象之前必须先知道该对象,这 对“编译器保证对象在使用时的类型正确性”是必须的。引用一个未知对象将会引起编译错误对象声明的作用是使程序知道该对象的类型和名字。它由关键字ext er n以及跟在后面的对象类型以及对象的名字构成。声明不是定义,不会引起内存的分配。实际上,它只是说明了在程序之外的某处有这个变量的定义变量的定义一个简单的对象定义由一个类型指示符后面跟一个名字构成,以分号结束一个简单的定义指定了变量的类型和标识符,它并不提供初始值。如果一个变量是在全局域内定义的,那么系统会保证给它提供初始值0。如果变量是在局部域内定义的,或是通过n ew表达式动态分配的,则系统不会向它提供初始值0。这些对象被称为是未初始化的。未初始化的对象不是没有值,而是它的值是未定义的初始的第一个值可以在对象的定义中指定,一个被声明了初始值的对象也被称为已经初始化的。C+支持两种形式的初始化,第一种形式是使用赋值操作符的显示语法形式,在隐式形式中,初始值被放在括号中3、指针类型指针持有另一个对象的地址,是我们能够间接地操作这个对象每个指针都有一个相关的类型,不同数据类型的指针之间的区别不是在指针的表示上,也不是在指针所持有的值上一一对所有类型的指针这两方面都是相同的。不同之处在于指针所指向的对象的类型上。指针的类型可以指示编译器怎样解释特定地址上内存的内容,以及该内存区域应该跨越多少内存单元我们通过在标识符前加一个解引用操作符(*)来定义指针。在逗号分割的标识符列表中,每个将被用作指针的标识符前都必须加上解引用操作符不允许不同指针类型之间相互赋值,因为不同类型的指针对内存的存储布局和内容的解释完全不同。C+提供了一种特殊的指针类型:(v o i d *)类型指针,它可以被任何数据指针类型的地址值赋值。v o i d *v表示相关的值是个地址,但该地址的对象类型不知道。我们不能够操作空类型指针所指的对象,只能传送该地址值或将它与其他地址值进行比较C+提供了解引用操作符(*)来间接地读和写指针所指向的对象指针可以让它的地址值增加或减少一个整数值,这类指针操作,被称为指针的算术运算4、字符串类型C+提供了两种字符串的表示:C风格的字符串和C+引入的S t r i n g类类型C风格字符串C风格字符串起源于C语言,并在C+中继续得到支持字符串被存储在一个字符数组中,一般通过一个c h a r*类型的指针操纵它。标准C库为操纵C风格字符串提供了一组函数。(标准C库作为标准C+的一部分被包含在其中)指向C风格字符串的字符指针总是指向一个相关联的字符数组C风格字符串的长度可以为0,有两种方式:字符指针被置为0,因而它不指向任何对象。或者,指针已经被设置,但是它指向的数组只包含一个空字符字符串类型标准C+提供了支持字符串操作的s t r i n g类S t r i n g类型能够自动将C风格的字符串转换成s t r i n g对象,(重载赋值操作符=)但是,反向的转换不能自动执行,为实现这种转换,必须显式地调用c _ s t r()操作5、c o n s t限定修识符c o n s t类型限定修识符把一个对象转换成一个常量。常量定义后就不能被修改,所以它必须被初始化,未初始化的常量定义将导致编译错误指 向c o n s t对象的指针试图将一个非c o n s t对象的指针指向一个常量对象的动作都将引起编译错误。我们必须声明一个指向常量的指针来指向一个常量对象,如:c o n s t i n t *c p t r;c o n s t对象的地址只能赋值给指向c o n s t对象的指针,但是,指 向c o n s t对象的指针可以被赋以一个非c o n s t对象的地址在实际的程序中,指 向c o n s t的指针常被用作函数的形式参数,它作为一个约定来保证:被传递给函数的实际对象在函数中不会被修改c o n s t指针指向非c o n s t对象的c o n s t指针:意味着不能赋给这种指针其他的地址值,但可以修改其指向的对象i n t *c o n s t c u r Er r =&e r r N u m b;指 向c o n s t对象的c o n s t指针:这种指针指向的对象的值以及它的地址本身都不能被改变6、引用类型引用有时候又被称为别名,它可以用作对象的另一个名字。通过引用我们可以间接地操纵对象,使用方式类似于指针。但是不需要指针的语法。在实际的程序中,引用主要被用作函数的形式参数,通常将类对象传递给一个函数引用类型由类型标识符和一个取地址操作符来定义,引用必须被初始化。一旦引用已经定义,它就不能再指向其他的对象。引用的所有操作实际上都被应用在它所指的对象身上,包括取地址操作符。c o n s t引用c o n s t i n t&引用变量名=初始化值;c o n s t引用可以用不同类型的对象初始化(只要能从一种类型转换到另一种类型即可),也可以是不可寻址的值,如文字常量,c o n s t引用是只读的。引用在内部存放的是一个对象的地址,它是该对象的别名。对于不可寻址的值,如文字常量,以及不同类型的对象,编译器为了实现引用,必须生成一个临时对象,引用实际上指向该对象,但用户不能访问它。指针和引用有两个主要区别:引用必须总是指向一个对象。如果用一个引用给另一个引用赋值,那么改变的是被引用的对象而不是引用本身7、布尔类型布尔型对象可以被赋以文字值t r u e和f a l s e当表达式需要一个算术值时,布尔对象和布尔文字都被隐式地提升成i n t:f a l s e变成0,而t r u e变 成1。如果有必要,算术值和指针值也能隐式地被转换成布尔类型的值。或空指针被转换成f a l s e,所有其他的值都被转换成t r u e8、枚举类型枚举定义了整数常量,而且还把它们组成一个集合e n u m o p e n _ m o d e s i n p u t=l,o u t p u t,a p p e n d ;o p e n _ m o d e s是一个枚举类型。每个被命名的枚举定义了一个唯一的类型,它可以被内作类型标识符。I n p u t是枚举成员,它们代表了能用来初始化和赋值o p e n _ m o d e s类型变量的值的全集。如果我们给枚举变量传递枚举成员之外的值,编译奖就会报错。而且,给枚举变量传递一个与枚举成员相等的整数值,编译器仍然会将其标记为错误我们不能做到的是打印枚举成员的实际枚举名。第二件不能做的事情是,我们不能使用枚举成员进行迭代,C+不支持在枚举成员之间的前后移动枚举类型用关键字e n u m,加上一个自选的枚举类型名来定义,类型名后面跟一个用花括号括起来的枚举成员列表,枚举成员之间用逗号分开。缺省情况下,第一个枚举成员被赋以值0,后面的每个枚举成员依次比前面的大1。我们也可以显式地把一个值赋给一个枚举成员我们可以定义枚举类型的对象,它可以参与表达式运算,也可以作为参数传递给函数。枚举类型的对象能够被初始化,但是只能被一个相同枚举类型的对象或枚举成员集中的某个值初始化或赋值。必要时.,枚举类型会自动提升成算数类型9、数组类型数组是一个单一数据类型对象的集合。其中单个对象并没有被命名,但是我们可以通过它在数组中的位置对它进行访问。这种访问形式被称为索引访问数组定义由类型名标识符和维数组成,维数指定数组中包含的元素的数目,它被写在一对方括号里边。我们必须为数组指定一个大于等于1的维数。维数值必须是常量表达式即必须能在编译时刻计算出它的值,这意味着非c o n s t的变量不能被用来指定数组的维数。C+没有提供编译时刻或运行时刻对数组下标的范围检查。除了程序员自己注意细节,并彻底地测试自己的程序之外,没有别的办法可防止数组越界数组与指针类型的关系数组标识符代表数组中第一个元素的地址它的类型是数组元素类型的指针1 0、v e c t o r容器类型v e c t o r类为内置数组提供了一种替代表示,与s t r i n g类一样,v e c t o r类是随标准C+引入的标准库的一部分使 用v e c t o r有两种不同的形式,即所谓的数组习惯和S TL习惯。在数组习惯用法中,我们模仿内置数组的用法:定义一个已知长度的v e c t o r:v e c t o r i v e c(1 0);v e c t o r的元素被初始化为与其类型相关的缺省值,算术和指针类型的缺省值是0,对 于c l a s s类型缺省值可通过调用这类的缺省构造函数获得。与内置数组不同,v e c t o r可以被另一个v e c t o r初始化,或被赋给另一个v e c t o r在S TL中,对v e c t o r的习惯用法完全不同。我们不是定义一个已知大小的v e c t o r,而是定义一个 空v e c t o r:v e c t o r t e x t;我们向v e c t o r中插入元素,而不再是索引元素,以及向元素赋值。例如,p u s h _ b a c k()操作,就是在v e c t o r的后面插入-一个元素1 1、t y p e d e f 名字t y p e d e f机制为我们提供了一种通用的类型定义设施,可以用来为内置的或用户定义的数据类型引入助记符号t y p e d e f定义以关键字t y p e d e f开始,后面是数据类型和标识符。这里的标识符即t y p e d e f名字,它并没有引入一种新的类型,而只是为现有类型引入了一个助记符号。1 2、类类型类的成员函数可以被定义在类的定义中,也可以定义在外面。我们应该把成员函数的定义放在一个程序文本文件中,并且把含有该类定义的头文件包含进来内联函数在每个调用点上被展开,因此,这样做可以消除函数调用相关的额外消耗。只要该函数被调用足够多次,内联函数就能够显著地提高性能。在类定义内部定义的成员函数在缺省情况下被设置为i n l i n e,在类外而定义的成员函数必须显式地声明为i n l i n e。在类体外定义的内联成员函数,应该被包含在含有该类定义的头文件中第4章表达式1、表达式表达式由一个或多个操作数(o p e r a n d)构成。最简单的表达式由一个文字常量或一个对象构成,一般地表达式的结果是操作数的右值在更一般的情况下表达式由一个或多个操作数以及应用在这些操作数上的操作构成应用在操作数上的操作由操作符(o p e r a t o r)表示作用在一个操作数上的操作符被称为一元操作符,比如取地址操作符(&)和解引用操作符(*)。作用在两个操作数上的操作符,比如加法操作符减法操作符被称为二元操作符。有些操作符既能表示一元操作也能表示二元操作确切地说,是相同的符号用来表示两个不同的操作表达式的计算是指执行一个或多个操作,最后产生一个结果除非特别声明一般来说,表达式的结果是个右值。算术表达式结果的类型由操作数的类型来决定。当存在多种数据类型时,编译器将根据一套预定义的类型转换规则集进行类型转换当两个或两个以上的操作符被组合起来的时候这样的表达式被称为复合表达式,是否能够成功地计算表达式要取决于子表达式的计算顺序。一般来说子表达式的计算顺序由操作符的优先级和结合性来决定2、算术操作符在某些实例中,算术表达式的计算会导致不正确或未定义的值,这些情况被称为算术异常,但是不会导致抛出实际的异常。算术异常要归咎于算术的自然本质(比如除以0)或归咎于计算机的自然本质一一比如溢出(指结果值超出了被赋值对象的类型长度)。s t a t i c _ c a s t (b y t e _ v a l u e )强制转软使编译器把一个对像或表达式从它当前的类型转换成程序员指定的类型3、等于、关系和逻辑操作等于、关系和逻辑操作符的计算结果是布尔常量t r u e或f a l s e。如果这些操作符用在要求整数值的上下文环境中,它们的结果将被提升成1或0当逻辑与(&)操作符的两个操作数都为t r u e时,结果值才会是t r u e。对于逻辑 或(H)操作符只要两个操作数之一为t r u e,它的值就为t r u e。这些操作数被保证按从左至右的顺序计算。只要能够得到表达式的值(t r u e或f a l s e)运算就会结束。二元关系操作符左右操作数的计算顺序,在标准C和C+中都是未定义的,因此计算过程必须是与顺序无关的4、赋值操作符赋值和初始化有时候会被混淆,因为它们都使用同一个操作符(=)一个对象只能被初始化一次,也就是在它被定义的时候,但是在程序中可以被赋值多次。当我们把不同类型的表达式赋值给一个对象时,编译器会试着隐式地将右操作数的类型转换成被赋值对象的类型,如果这种类型转换是有可能的。如果不可能进行隐式的类型转换,那么赋值操作被标记为编译时刻错误。赋值操作符的左操作数必须是左值一一即它必须有一个相关联的可写的地址值,赋值的结果是实际上被放在左操作数相关内存中的值赋值操作符也可以被连接在一起,只要每个被赋值的操作数都是相同的数据类型C+提供了一套复合赋值操作符,复合赋值操作符的一般语法格式是a o p=b;这里的o p=可以是下列十个操作符之一+=,-=,*=,/=,%=,=,&=,=,|=5、复数操作标准库提供的复数co m p l e x类是基于对象的类抽象的完美模型。通过使用操作符重载,我们几乎可以像使用简单内置类型一样容易地使用复数类型的对象6、条件操作符条件操作符为简单的i f-e l s e语句提供了一种便利的替代表示法,条件操作符的语法格式如下:e x p r l?e x p r 2 :e x p r 3;e x p r l的计算结果不是t r u e就 是f a l s e。如果它是t r u e,则e x p r 2被计算,否则e x p r 3被计算7 s i z e o f操作符s i s e o f操作符的作用是返回一个对象或类型名的字节长度。它有以下三种形式:s i z e o f (t y p e n a m e );s i z e o f (o b j e ct );s i z e o f o b j e ct;返回值的类型是s i z e _ t这是一种与机器相关的t y p e d e f定义我们可以在cs t d d e f头文件中找到它的定义。当s i z e o f操作符应用在数组上时例如上面例子中的i a,它返回整个数组的字节长度,而不是第一个元素的长度,也 不是i a包含的元素的个数应用在指针类型上的s i z e o f操作符返回的是包含该类型地址所需的内存长度,但是应用在引用类型上的s i z e o f操作符返回的是包含被引用对象所需的内存长度s i z e o f操作符在编译时刻计算,因此被看作是常量表达式。它可以用在任何需要常量表达式的地方。如数组的维数或模板的非类型参数8、n e w和d e l e t e表达式系统为每个程序都提供了一个在程序执行时可用的内存池。这个可用内存池被称为程序的空闲存储区或堆。运行时刻的内存分配被称为动态内存分配。动态内存分 配 由n e w表达式应用在一个类型指示符上来完成,类型指示符可以是内置类型或用户定义类型。n e w表达式返回指向新分配的对象的指针例如:i n t *p i =n e w i n t :从空闲存储区中分配了一个i n t型的对象,并用它的地址初始化p i。在空闲存储区内实际分配的对象并没有被初始化。我们可以如下指定一个初始值:i n t *p i =n e w i n t(1 0 2 4 );它不但分配了这个对象而且用1 0 2 4将其初始化。要动态分配一个对象数组我们可以写成:i n t *p i a =n e w i n t 1 0 ;它从空闲存储区中分配了一个数组其中含有1 0个i n t型对象并用它的地址初始化p i n,而数组的元素没有被初始化。没有语法能为动态分配的数组的元素指定一个显式的初始值集合(在类对象数组的情况下如果我们定义了缺省构造函数,那么它将被顺次应用在数组的每一个元素上)所有从空闲存储区分配的对象都是未命名的,这是它的另一个特点。n e w表达式并不返回实际被分配的对象,而是返回这个对象的地址。对象的所有操作都通过这个地址间接来完成当对象完成了使命时,我们必须显式地把对象的内存返还给空闲存储区。我们通过把d e l e t e表达式应用在指向我们用n e w表达式分配的对象指针上来做到这一点,d e l e t e表达式不应该被应用在不是通过n e w表达式分配的指针上。例如:d e l e t e p i;释放了 P i指向的i n t对象,将其返还给空闲存储区类似地d e l e t e p s;在p s指向的s t r i n g类对象上应用s t r i n g的析构函数后,释放其存储区并将其返还给空闲存储区。最后:d e l e t e p i a;释放了 p i a指向的1 0个i n t对象的数组,并把相关的内存区返还给空闲存储区。在关键字d e l e t e与指针之间的空方括号表示d e l e t e的一种特殊语法,它释放由n e w表达式分配的数组的存储区9、逗号操作符逗号表达式是一系列由逗号分开的表达式。这些表达式从左向右计算。逗号表达式的结果是最右边表达式的值1 0、位操作符位操作符:,|,&=,=,|=位操作符把操作数解释成有序的位集合,这些位可能是独立的,也可能组成域。每个位可以含有0或1。位操作符允许程序员设置或测试独立的位或位域。如果个对象被用作一组位或位域的离散集合。那么这样的对象称为位向量。位向量是一种用来记录一组项目或条件的是/否信息的紧缩方法例如,在编译器中,类型声明的限定修饰符,如c on s t和vol a t il e,有时就被存储在位向量中。ios t re a m库用位向量表示格式状态,例如输出的整数是以十进制、十六进制,还是八进制显示在C+中,有两种方式支持位向量。在C语言和标准C+之前,它用内置整值类型来表示位向量,典型的情况是用un s ig n e d in t。标准库提供了一个bit s e t类,它支持位向量的类抽象。按位非操作符()翻转操作数的每一位,每 个1被设置为0,而每个0被设置为1移位操作符(,)将其左边操作数的位向左或右移动某些位。操作数中移到外面的位被丢弃。左移操作符()从右边开始用0补空位。如果操作数是无符号数,则右移操作符()从左边开始插入0,否则的话,它或者插入符号位的拷贝,或者插入0,这由具体实现定义按位与操作符(&)需要两个整值操作数。在每个位所在处如果两个操作数都含有1,则结果该位为1。否则为0按位异或操作符 O 需要两个整值操作数。在每个位所在处如果两个操作数只有一个含有1,则结果该位为1,否则为o按位或操作符(|)需要两个整值操作数。在每个位所在处,如果两个操作数有一个或者两个含有1,则结果该位为1,否则为0。1 1、bit s e t 操作要使用bit s e t类,我们必须包含相关的头文件:#in c l ud e bit s e t有三种声明方式。在缺省定义中我们只需简单地指明位向量的长度。例如:bit s e t bit ve c;还有两种方法可以构造bit s e t对象,它们都提供了将某位初始化为1的方式。一种方法是,为构造函数显式地提供一个无符号参数。bit s e t对象的前N位被初始化为参数的相应位值。例如:bit s e t bit ve c 2(O x f f f f );将bit ve c 2的 低1 6位设为1我们还可以传递一个代表0和1的集合的字符串参数来构造bit s e t对象,如下所示:/与bit ve c 3的初始化等价s t rin g bit va l(“1 0 1 0”);bit s e t bit ve c 4(bit va l );bit ve c 4和bit ve c 3的 第1和3位都被设置为1,而其他位保持为0我们还可以标记用来初始化bit s e t的字符串的范围。例如,在下面的语句中:/从位置6开始,长度为4:1 0 1 0s t rin g bit va l (,z1 1 1 1 1 1 0 1 0 1 1 0 0 0 1 1 0 1 0 1 0 1,/);bit s