C++语言面向程序设计03版_第1页
C++语言面向程序设计03版_第2页
C++语言面向程序设计03版_第3页
C++语言面向程序设计03版_第4页
C++语言面向程序设计03版_第5页
已阅读5页,还剩281页未读 继续免费阅读

下载本文档

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

文档简介

C+面向对象程序设计,目录,第1章C+基础第2章类和对象(一)第3章类和对象(二)第4章友元第5章运算符重载第6章模板第7章继承和派生第8章虚函数和多态性第9章C+流第10章异常处理和名字空间第11章C+标准模板库基础第12章面向对象软件设计,第1章C+基础,C+概述,计算机语言种类,计算机语言的种类非常多,总的来说可以分成机器语言、汇编语言、高级语言三大类。,计算机所能识别的语言只有机器语言,即由0和1构成的代码。但通常人们编程时,不采用机器语言,因为它非常难以记忆和识别。,目前通用的编程语言主要有两种形式:汇编语言和高级语言。,高级语言所编制的程序不能直接被计算机识别,必须经过转换才能被执行,按转换方式可将它们分为解释方式和编译方式两类。,程序设计方法,程序设计是指设计、编写和调试程序的方法与过程。,1.结构化程序设计,结构化程序设计方法的核心包括自顶向下、逐步求精的开发方法、模块化的组织方式和结构化的语句结构等几方面。,结构化程序设计的特点如下。,程序设计:程序是由一个个的函数组成的,函数之间通过调用而相互作用。程序设计的主要技巧在于追踪哪些函数和调用哪些函数,哪些数据发生了变化。程序内容:由函数和函数调用构成。,结构化设计的弱点表现在抽象级别较低、封装性较差、可重用性较低等方面。,2.面向对象程序设计,面向对象程序设计方法的核心包括抽象、封装、对象、类、消息和继承等几方面。,面向对象程序设计的特点如下。,程序设计:程序是由一个个的对象组成的,对象之间通过消息而相互作用。程序设计的主要技巧在设计哪些类以及类之间的关系。程序内容:由类对象和消息传递构成。,面向对象程序设计的优点表现在实现对现实世界客体的自然描述、可控制程序的复杂性、可增强程序的模块性、可提高程序的重用性和可改善程序的可维护性等方面。,C+语言及其特点,1.C+语言的起源,C+语言是一种高级语言,它是对C语言的扩展,是C语言的超集。贝尔实验室的BjarneStroustrup在C语言的基础上,创建了C+语言。,C+语言的ISO标准已在1997年11月被一致通过,1998年8月被正式批准。,2.C+语言的特点,(1)C+支持数据封装(2)C+类中包含私有、公有和保护成员(3)C+中通过发送消息来处理对象(4)C+中允许友元破坏封装性(5)C+允许函数名和运算符重载(6)C+支持继承性(7)C+支持动态绑定,C+程序的基本结构,C+程序的基本结构如下。,(1)C+程序由函数组成(2)C+函数由函数首部与函数体两部分组成(3)C+程序的书写格式(4)C+的输入输出(5)C+注释(6)编译预处理命令,C+程序的开发步骤,C+是一种编译性的语言,设计好一个C+源程序后,需要经过编译、连接,生成可执行的程序文件,然后执行并调试程序。C+程序的开发步骤如下:,(1)分析问题(2)编辑程序(3)编译程序(4)连接程序(5)运行调试程序,C+程序开发步骤如图所示。,C+语言初识,数据类型,C+的基本数据类型有5种:整型(int)、浮点型(float)、字符型(char)、双精度浮点型(double)和无值型(void)。,C+提供的5种基本数据类型,其数据的长度和范围会随处理器的类型和编译器类型的不同而异。一般来说,字符类型为1个字节长;整数类型与CPU字长相等,一般为2个字节或4个字节长;浮点数的长度一般为整型数的2倍;双精度类型字长为浮点型的2倍。,C+的修饰符有short、long和unsigned。,下表列出了大多数32位系统中内置数据类型的范围。,常量,常量是在程序中不能改变的量。C+支持4种类型的常量:浮点型、整型、字符型和枚举型。,变量,顾名思义,变量就是值可变的量。一个变量有三个要素:,变量由一个变量名惟一标识,也就是说,每个变量有一个变量名。变量可以保存某个数据值,而数据值有相应的数据类型,所以每个变量又具有一个特定的数据类型。数据类型标志着这个变量将来的用法以及它将保存哪种类型的数据值。变量存储在内存中,有对应的地址,也就是说,每个变量有一个地址,可以进行取地址等操作,而其他表达式如x+y不能进行取地址操作。,(1)变量的命名(2)变量的定义和声明(3)变量的初始化,数据的输入和输出,输入输出是程序的基本功能。C+的标准输入/输出流库用于数据的输入输出,用于输入输出的流库包含在头文件iostream.h中。,1.输出,C+定义了运算符“”的iostream类,而“”送到程序指定的变量中。因此,用户输入数据时要避免输入的数据超出指定变量的值域。,控制语句,顺序控制语句,所谓顺序结构,就是按照语句的顺序一条一条地执行。顺序控制语句是一类简单的语句,包括表达式语句、复合语句、空语句和输入输出语句等。,选择控制语句,除简单的顺序控制语句外,C+还定义了一些可以控制程序执行流程的语句,这些语句提供对控制流的选择和循环功能。C+中,语句默认都是顺序执行,如果碰到选择或循环语句,顺序执行的规则就要改变。,C+中的选择控制语句有if语句、ifelse语句、ifelseif语句和switch语句。,循环控制语句,循环控制语句提供重复处理的能力,当某一特定条件为真时,循环语句就重复执行,并且每循环一次,就会测试一下循环条件,如果为假,则循环结束,否则继续循环。,C+支持三种格式的循环控制语句:while、do和for语句。三者可以完成类似的功能,不同的是它们控制循环的方式。,跳转语句,除了顺序执行和选择、循环控制外,有时需要中断一段程序的执行,跳转到其他地方继续执行,这时需用到跳转语句,包括break、continue和goto语句。,构造数据类型,数组类型,数组是具有相同数据类型的元素序列。在内存中,它占据一组连续的内存位置。数组的每一项都是一个变量,称为元素。每个元素的存取是通过数组名加偏移来实现的。实际上,数组是一组相关的内存位置,它们都具有相同的名字和类型。为了引用数组中的特定位置或元素,需指定数组名和数组中特定元素的位置编号。,数组是程序设计语言中常用的数据结构之一。当若干数据具有相同的数据类型并且互相有一定关系时,把它们组织成数组非常有效。,和其他变量一样,在使用数组之前需要对数组进行定义。数组分为一维数组、二维数组和三维及以上的数组,通常把二维数组称为矩阵,三维及以上的数组称为多维数组。,枚举类型,枚举类型定义了一些整型符号常量的集合,其格式如下:,enum类型名标识符1,标识符2,标识符n;,其中,“标识符1”至“标识符n”表示一些整型符号常量,它们默认由系统赋予整数值:“标识符1”的值为0,“标识符2”的值为1,“标识符n”的值为n-1,序列中每个标识符的值比前一个标识符大1。,程序员也可以自己定义标识符的值,定义方式是直接在定义语句的标识符后赋值。,结构体类型,在日常生活中经常会遇到这样的情况,由一些相关的数据共同表示一个信息。例如,表示一个学生的信息,可以包括学生的学号、姓名、性别、出生日期、班号等相关的数据项,这些数据项的组合表示一个学生的个人情况,它们之间是有内在联系的,任何一个单独的信息都不能完整地表示学生的信息。在C+中用结构体来表示这些相关的信息。构成结构体的成员的数据类型可以不同,它们形成了一个整体,成为一个新的数据类型,称为结构体类型。结构体类型由各个成员构成,有时也将这些成员称为数据域,简称为域。,结构体类型和结构体变量的声明格式如下:,struct结构体名/结构体类型声明成员类型成员1;成员类型成员2;.成员类型成员n;结构体变量名/结构体变量定义,其中,“结构体名”和“结构体变量名”都可以省略。省略前者表示声明了一个无名结构体类型和定义了具有该类型的变量,省略后者表示仅仅声明了一个结构体类型。,共用体类型,共用体的定义形式和用法类似于结构体,但结构体中的每个成员在内存中占有独立的存储位置,而共用体的各个成员共享一块内存,所以,在任意时刻只能有一种数据类型的变量是活跃的。,共用体类型和共用体变量的声明格式如下:,union共用体名/共用体类型声明成员类型成员1;成员类型成员2;.成员类型成员n;共用体变量名;/共用体变量定义,其中,“共用体名”和“共用体变量名”都可以省略。省略前者表示声明了一个无名共用体类型和定义了具有该类型的共用体变量,省略后者表示仅仅声明了一个共用体类型。,用户自定义类型,在C+中可以利用typedef定义自己的变量类型,其格式如下:,typedef类型声明;,其中,“类型声明”类似于变量声明,只是变量名用类型名代替了,实际上,这等于定义了一个同义词,这种方式与创建一个新的类型是不同的。,使用自定义类型有几点好处:一是可以更好地表达程序员的意思,如用width来表示将要定义的标识符是属于宽度一类的数据,用string表示程序员想要定义的是一个字符串,这比直接用系统预定义的类型清晰得多;二是简单方便,一些数据类型可以用一个简单的类型标识符来表示,不用每次都麻烦地写复杂的定义。,指针,指针的定义,定义指针时必须指明指针所指对象的数据类型,且在变量名前加上星号“*”表示这是一个指针。当定义多个同一类型的指针时,用逗号隔开各变量标识符,且每个变量前都要加上星号。,一个指针所占用的内存空间大小与一个内存地址所占空间等同,因而,一个浮点型指针和一个字符型指针所占内存大小相同。指针的类型仅用于告知编译系统如何将指针所指的二进制序列翻译成实际的数据。,指针的初始化,指针初始化有下列几种方式。,(1)指针对象可以被一个具有相同类型的对象初始化。,(2)由另一个同一类型的指针初始化,同一类型的指针之间可以直接赋值。,(3)通过直接分配内存地址得到初值。,指针的运算符,定义指针变量的目的是,通过指针变量间接地访问变量。为此C+提供了两个指针运算符。,*:取指针值运算符。根据指针所指内存单元地址间接地访问对应存储单元,若指针变量p指向变量a,则*p运算的结果为变量a的值,即*p表示变量a的内容。/全局变量fun()intvar;/局部变量var=:var;/将全局变量的值赋给局部变量,断言,assert宏(在assert.h头文件中定义)测试表达式的值。如果表达式的值是0(假),则assert打印错误信息,并调用函数abort()以结束程序的执行。这是测试某个变量是否具有正确值的有用的调试工具,例如,以下语句就是一个断言:,assert(x10);,当程序中遇到这个语句时,如果x的值大于或等于10,则将打印包含行号和文件名的错误信息,而且程序终止。,练习题1,给出以下程序的执行结果。#includevoidmain()inti,j,k;for(i=1;i=6;i+)for(j=1;j=20-2*i;j+)cout;for(k=1;k=i;k+)couti;coutendl;,上机实验题1,编写一个程序,将用户输入的由数字字符和非数字字符组成的字符串中的数字提取出来,例如输入asd123rt456,fg789,则产生的数字分别是123、456和789。,第2章类和对象(一),类,类的声明,类是一种用户自定义的数据类型,声明类的一般格式如下:,class类名private:私有数据成员和成员函数;protected:保护数据成员和成员函数;public:公有数据成员和成员函数;各个成员函数的实现;,类界面,类实现,在声明类时有如下规则:,如果类的成员是变量,可以像声明变量一样声明它。如果类的成员是函数,一般是使用函数原型来声明它。如果类的成员是函数,它可以访问类中的任何成员数据成员和成员函数。也就是说,当声明类的成员函数时,定义的函数可以直接访问该类中任何成员而无需将其声明为参数,惟一的限制条件是在使用一个成员之前必须声明它。,类的组织形式,通常将类界面与类实现分离,将类界面部分存放在头文件(.h)中,将类实现放在程序文件(.cpp)中,而使用类的程序放在另一个程序文件中,这样使整个程序更清晰。例如,声明类C的类界面的部分用c.h文件保存,类实现部分用c.cpp文件保存,而使用类C的部分用a.cpp保存,如图所示。,类的作用域,声明类时所使用的一对大括号()形成了类作用域。在类作用域中声明的标识符只在该类中具有可见性,并且其作用域与该标识符声明的次序无关。,类作用域包括了类中成员函数的作用域,即使该成员函数的实现放在类的外面也是如此。所以当成员函数的函数体中使用一个标识符时,编译系统首先在成员函数中寻找其声明,如果未找到则在该成员函数所在的类中寻找,如果还未找到,则在包含类作用域的更大作用域中作最后寻找。,类的成员函数,类的成员函数用于实现某种操作,成员函数的定义体可以在类的声明体中,也可以在类的说明体外。,在类声明体中实现的函数是内联函数。在类声明体外实现的函数可以通过在函数声明和定义上分别加上inline来表示该函数是内联的,否则不是内联函数。,在类的声明体内定义成员函数的优点是使整个类集中于程序代码的同一位置上,不利的方面是增加了类声明的规模和复杂性,而且,内联的函数代码并不被相同类的对象所共享,因而增大了程序的内存开销。,类的访问权限,在类声明中,public、private和protected是关键字,称为成员访问限定符,它们分别表示公有、私有和保护的成员访问权限。,在C+中,有关类的访问权限的其他规定如下:,在默认的情况下,一个类中所有的成员都是私有的。一旦给出了成员访问限定符(如public:),它后面的成员都具有这个成员访问权限(如后面的成员均为公有的),直到出现另一个成员访问限定符或类声明结束为止。,类与结构体类型的区别,从类的声明格式可以看出,类与结构体类型是非常相似的。C+中类是由结构体类型演化而来的,但对结构体类型进行了扩展。类的成员可以是数据成员或成员函数,结构体中的成员也可以是数据成员或函数成员。并且在结构体中,也可以使用关键词public、private、protected限定其成员的访问权限。,实际上,结构体只是类的一个特例。结构体与类惟一区别在于:在类中,其成员的默认访问权限是私有的,而在结构体类型中,其成员的默认访问权限是公有的。,当只需要描述数据结构时,使用结构体较好。当需要描述数据又需要描述对数据的处理方法时使用类为好。,类的特点,类具有如下特点。,(1)类具有封装性(2)类具有安全性(3)类具有独立性与可维护性(4)类具有继承性(5)类具有多态性,对象,对象的定义格式,一旦声明了一个类,就可以用它作为数据类型来定义类对象(简称为对象)。在C+中,类对象也称为类变量或者类实例。,定义类对象的格式如下:,类名对象名表;,其中,“类名”是待定的对象所属的类的名字,即所定义的对象是该类的对象。“对象名表”中可以有一个或多个对象名,多个对象名之间用逗号分隔。在“对象名表”中,可以是一般的对象名,还可以是指向对象的指针变量名(即对象指针)、引用名(即对象引用)、对象数组名。,对象的数据成员访问方法,对象的数据成员的访问方式如下:,对象名.数据成员名,其中,“.”是一个运算符,该运算符的功能是表示对象的成员。,对象的成员函数调用方法,成员函数的调用是为了响应发送对象的消息。消息对应于从一个对象向另一个对象或者从一个函数向一个对象发送的成员函数调用。,调用对象的成员函数的方式如下:,对象名.成员名(参数表),对象的存储空间,实际上,C+只为每一个对象的数据成员分配内存空间,类中的所有成员函数只生成一个副本,而该类的每个对象执行相同的函数成员副本。因此在描绘类图时,通常将显示类中所有的成员。然而在描绘类的对象图时,将只显示其数据成员。,实际上,类对象也像变量一样,可以定义全局对象或静态对象等,它们的存储空间类型和作用域与全局变量或静态变量是一样的。,对象的赋值运算,在定义了一个类的多个对象时,可以在对象之间进行赋值运算,也称为对象复制,假设定义了例2.1中类MyClass的两个对象sa1和sa2,它们的值如图所示。,构造函数,什么是构造函数,C+提供了利用类的构造函数来初始化类的数据成员。,构造函数具有如下性质:,构造函数的名字与类的名字相同。构造函数尽管是一个函数,但没有任何类型,即它既不属于返回值函数也不属于void函数。类可以有多个构造函数。然而,一个类的所有构造函数的名字都相同。如果类有多个构造函数,则它们的参数是各不相同的。当类对象创建时,构造函数会自动地执行;由于它们没有类型,不能像其他函数那样进行调用。当类对象说明时调用哪一个构造函数取决于传递给它的参数类型。,调用构造函数,当定义类对象时,构造函数会自动执行。因为一个类可能会有包括默认构造函数在内的不止一种构造函数,下面讨论如何调用特定的构造函数。,1.调用默认构造函数,假设一个类包含有默认构造函数,调用默认构造函数的语法如下:,类名类对象名;,2调用带参数的构造函数,假设一个类中包含有带参数的构造函数,调用这种带参数的构造函数的语法如下:,类名类对象名(参数表),其中,“参数表”中的参数可以是变量,也可以是表达式。,3.调用内置数据类型的构造函数,实际上,C+中内置的数据类型都可以看成是类,定义变量时除了可以使用“=”运算符赋初值外,还可以像定义类对象一样调用其构造函数给变量赋初值。,4.用new动态创建对象,可以用new运算符来动态地建立对象。用new运算符建立对象时,同样也要自动调用构造函数,以便完成对象数据成员的初始化。,5.用构造函数初始化对象的过程,构造函数初始化对象的过程,实际上就是对构造函数的调用过程。一般情况下按如下步骤进行:,(1)程序执行到定义对象语句时,系统为对象分配内存空间。(2)系统自动调用构造函数,将实参传送给形参,执行构造函数体时,将形参值赋给对象的数据成员。完成数据成员的初始化工作。,重载构造函数,构造函数可以像普通函数一样被重载,C+根据声明中的参数个数和类型选择合适的构造函数。,复制构造函数,复制构造函数常用于将一个已知对象的数据成员复制给正在创建的另一个同类的对象。除此之外,复制构造函数可以像其他构造函数一样定义和使用。,复制构造函数的格式如下:,类名:复制构造函数(类名(3)在引用静态数据成员时采用格式类名:静态数据成员,静态成员函数,静态成员函数与静态数据成员类似,也是从属于类,都是类的静态成员。只要类存在,静态成员函数就可以使用,静态成员函数的定义是在一般函数定义前加上static关键字。调用静态成员函数的格式如下:类名:静态成员函数名(参数表);,类成员指针,类数据成员指针,类数据成员指针的定义格式如下:,类型类名:*指针名,由于类不是在运行时存在的对象,因此在使用类数据成员指针时,需要首先指定类的一个数据成员,然后通过类的对象来引用指针所指向的成员。,类成员函数指针,指向类成员函数指针的定义格式如下:类型(类名:*指针名)(参数表)给类成员函数指针赋值的格式如下:指向函数的指针名=函数名程序中使用指向函数的指针调用函数的格式如下:(*指向函数的指针名)(实参表),应用实例,编写一个程序,设计一个满足如下要求的CDate类,用数据进行调试并输出结果:,(1)用日/月/年格式输出日期。(2)可进行日期加一天的操作。(3)设置日期。,练习题2,给出以下程序的执行结果。,#includeclassSampleintx,y;public:Sample()x=y=0;Sample(inta,intb)x=a;y=b;Sample()if(x=y)coutx=yendl;elsecoutx!=yendl;voiddisp()coutx=x,y=yendl;voidmain()Samples1(2,3);s1.disp();,上机实验题2,设计一个词典类Dic,其中包含若干单词信息,每个单词由英文单词及对应的中文含义组成。并含有单词增加和英汉翻译成员函数,通过查词典的方式将一段英语翻译成对应的汉语。,第3章类和对象(二),常对象和常对象成员,常对象,常对象是指对象常量,其一般定义格式如下:,类名const对象名;或者:const类名对象名;,使用常对象成员时需要注意以下几点:,在定义常对象时必须进行初始化。常对象的数据成员不能被更新。,常对象成员,常对象成员包括常成员函数和常数据成员。,1.常成员函数,使用const关键词声明的函数为常成员函数,常成员函数声明格式如下:,类型函数名(参数表)const;,使用常对象成员时需要注意以下几点:,const是函数类型的一个组成部分,因此在实现部分也要带const关键词。常成员函数不更新对象的数据成员,也不能调用该类中没有用const修饰的成员函数。如果将一个对象说明为常对象,则通过该常对象只能调用它的常成员函数,而不能调用其他成员函数。const关键词可以参与区分重载函数。例如,在类中有如下说明:“voidprint();voidprint()const;”,则这是对print的有效重载。,2.常数据成员,与一般数据相同,类的成员数据也可以是常量和常引用,使用const说明的数据成员称为常数据成员。如果在一个类中说明了n个常数据成员,那么构造函数就只能通过初始化列表对该数据成员进行初始化,其一般格式如下:,构造函数(参数表):常数据成员1(参数1),常数据成员2(参数2),常数据成员n(参数n),其中,冒号后面是一个数据成员的初始化列表,它包含一个初始化项,当有多个初始化项时,要用逗号分隔开。“参数1”“参数n”均为“参数表”中的某个参数。这样,在执行构造函数时自动将“常数据成员1”赋值为“参数1”的值,“常数据成员2”赋值为“参数2”的值,“常数据成员n”赋值为“参数n”的值。,类对象数组,对象数组是指一个数组元素都是对象的数组,也就是说,若某一个类有若干个对象,则可以把这一系列被创建的对象用一个数组来存放。,若要定义一个带有构造函数的类的对象数组,那么这个类必须含有一个不带参数的构造函数或带有缺省参数的构造函数,这是因为对象数组被创建时无法对构造函数传递参数,只能调用默认构造函数来初始化(数组中的)每一个类对象。,假设定义了包含100个对象的数组,那么给每一个类对象指定不同的构造函数是不现实的。,假如己声明了一个类MyClass,则有以下语句:,MyClasss100;,子对象,has-a关系,如果类A的声明中将类B的对象作为数据成员,也就是说,类A有一个类B的对象(即子对象),则称类A和类B之间是一种has-a关系,通常用图3.1表示,从类B的结点画一条线到类A的结点,并在类A结点的一端标记一个实心小圆。has-a关系是一种组合关系,下图表示类A对象是由类B对象组成的。,两个类has-a关系的图示,子对象构造函数的设计和执行次序,当含有子对象的类存在构造函数时,情况就比较复杂,特别要注意构造函数的调用次序。设类A含有子对象obj,该子对象对应的类为B,如下所示:,classB;classABobj;/obj是类B的对象,是类A的子对象;,在一个类的定义中,若定义有n个对象成员,则其构造函数的一般格式为:,类名:类名(形参表):对象名1(实参表1),对象名2(实参表2),对象名n(实参表n)构造函数体,其中:“对象名1(实参表1),对象名2(实参表2),对象名n(实参表n)”称为成员初始化表。对该构造函数的几点说明如下:,形参必须带有类型声明,而实参是可计算值的表达式。对象成员构造函数的调用顺序取决于这些对象成员在类中的声明顺序,而与它们在成员初始化表的位置无关。建立类的对象时,先调用各个对象成员的构造函数,初始化相应的对象成员,然后才执行类的构造函数,初始化类中其他成员。析构函数的调用顺序与构造函数正好相反。,子对象析构函数的设计和执行次序,同样,当含有子对象的类存在析构函数时,特别要注意析构函数的调用次序。在含有子对象的类A中,设计析构函数如下:,A()函数体;,其执行次序是:先执行函数体,再以子对象在类A中说明的相反次序调用各类的析构函数。,前向引用声明,C+的类应当先声明,然后再使用。但是在处理相对复杂的问题时,很可能遇到两个类相互引用的情况,这时候,就必然要有一个类在定义之前就被引用,解决这一问题的方法是使用前向引用声明。,前向引用声明是在引用未定义的类之前对该类进行声明,它只是为程序引入一个代表该类的标识符,类的具体定义可以在程序的其他地方。,嵌套类,可以在一个类声明中声明其他类,这种在其他类中声明的类称为嵌套类。其层次结构如下:,classOuterClasspublic:private:classInnerClass;,嵌套类可以是公有的,也可以是私有的。,由于嵌套类是在外层类的范围内声明的,有时候嵌套类(如上面所示的类InnerClass)也可能在外层类之外使用。如果嵌套类是公有的,那么嵌套类也可以在外层类之外作为一种数据类型使用,但此时嵌套类的类型应该是OuterClass:InnerClass。,局部类,一个类的声明还可以是在某一个函数的定义中,这种类称为局部类,局部类的声明限制在函数定义中。局部类一般不包含静态成员。,this指针,this是一个隐含于每一个类的成员函数的特殊指针,它是由VC+系统内部设定的,不需要程序员另外定义,可以直接使用。该指针是一个指向正在被某个成员函数操作的对象的指针。,当一个对象调用成员函数时,编译程序先将对象的地址赋给this指针。,this指针是C+实现封装的一种机制,它将对象和该对象调用的成员函数连接在一起,在外部看来,每个对象都拥有自己的成员函数。,应用实例,编写一个程序,设计一个点类Point和一个距离类Distance,后者的数据成员包括了Point类的两个对象p1和p2,并计算这两个点的距离。,练习题3,给出以下程序的执行结果。,#includeclassSampleintm,n;public:Sample()/构造函数Sample(inti,intj)m=i,n=j;/重载构造函数intadd()returnm+n;voidmain()inti;Samplea3=Sample(0,1),Sample(3,4),Sample(6,8);for(i=0;i3;i+)coutai.add();cout”运算符是成员访问运算符,这种单目运算符只能被重载为成员函数。一般成员访问运算符的使用格式如下:,对象-成员,成员访问运算符“-”函数重载的一般形式为:,数据类型类名:operator-();,重载双目运算符,重载双目运算符为成员函数,假设有一个类A,对于双目运算符op,如果重载运算符op使之能够实现表达式“obj1opobj2”,其中obj1和obj2均为A类的对象。,若把op重载为A类的成员函数,该函数只有一个形参,形参的类型是obj2所属的类型。经过重载之后,表达式“obj1opobj2”解释为:,obj1.operatorop(obj2),左边的对象obj1通过this指针传递,右边的对象obj2由参数传递。,重载双目运算符为友元函数,运算符也可以重载为类的友元函数,这样,它就可以自由地访问该类的任何数据成员。,假设有一个类A,对于双目运算符op,如果重载运算符op使之能够实现表达式“obj1opobj2”,其中obj1和obj2均为A类的对象。,若把op重载为A类的友元函数,该函数有两个形参,经过重载之后,表达式“obj1opobj2”解释为:,operatorop(obj1,obj2),左右两个对象obj1和obj2都由参数传递。,重载比较运算符,除了单目运算符和双目运算符重载外,也可以设计比较运算符(如“”、“”或“=”)的重载函数。比较运算符重载函数必须返回true(非0)或false(0)。,重载赋值运算符,重载“+=”和“-=”运算符,对于标准数据类型,“+=”和“-=”的作用是将一个数据与另一个数据进行加法或减法运算,然后再将结果回送给赋值号左边的变量中。对它们重载后,使其实现其他相关的功能。,重载“=”运算符,赋值运算符“=”的原有含义是将赋值号右边表达式的结果复制给赋值号左边的变量,通过运算符“=”的重载将赋值号右边对象的数据成员依次复制到赋值号左边对象的数据成员中。在正常情况下,系统会为每一个类自动生成一个默认的完成上述功能的赋值运算符,当然,这种赋值只限于由一个类类型说明的对象之间赋值。,如果一个类包含指针成员,采用这种默认的按成员赋值,那么当这些成员撤销后,内存的使用将变得不可靠。,重载下标运算符,下标运算符“”通常用于获取数组的某个元素,重载下标运算符可以实现数组下标的越界检测等。下标运算符重载函数只能作为类的成员函数,不能作为类的友元函数。,重载new与delete运算符,C+提供了new与delete两个运算符用于内存管理,在大多数情况下,它们是非常有效的。但有些情况下我们需要自己管理内存,以克服new与delete的不足。这就要重载运算符new与delete,使其按照要求完成对内存的管理。,new和delete只能被重载为类的成员函数,不能重载为友元。而且,无论是否使用关键字static进行修饰,重载了的new和delete均为类的静态成员函数。,运算符new重载的一般格式如下:,void*类名:operatornew(size_t,参数表);,new重载应返回一个无值型的指针,且至少有一个类型为size_t(size_t是C+系统内建类型)的参数。若该重载带有多于一个的参数,则其第一个参数的类型必须为size_t。,在带有“参数表”时,应注意使用重载new的方式。,重载逗号运算符,逗号运算符是双目运算符,和其他运算符一样,我们也可以通过重载逗号运算符来完成期望完成的工作。逗号运算符构成的表达式为“左运算数,右运算数”,该表达式返回右运算数的值。如果用类的成员函数来重载逗号运算符,则只带一个右运算数,而左运算数由指针this提供。,重载类型转换运算符,C+中提供了标准类型的相互转换,如执行语句:,n=(int)1.87;则n=1。同样,可以进行这种类型转换运算符重载。这种重载运算符函数的格式如下:operator类型名()函数体;,与以前的重载运算符函数不同的是,类型转换运算符重载函数没有返回类型,因为“类型名”就代表了它的返回类型,而且也没有任何参数。在调用过程中要带一个对象实参。,另外,转换运算符重载的缺点是无法定义其类对象运算符操作的真正含义,因为只能进行相应对象成员数据和一般数据变量的转换操作。,重载函数调用运算符,函数调用运算符“()”只能说明成类的非静态成员函数,该函数具有以下的一般格式:,数据类型类名:operator()(参数表);,与普通函数一样,重载了的函数调用运算符可以事先带有零个或多个参数,但不得带有缺省的参数。,应用实例,编写一个程序,声明一个点类Point,实现点的偏移、“=”、“!=”、“+=”、“-=”、“+”、“-”和输出等运算。并用若干数据进行测试。,练习题5,给出以下程序的执行结果。,#includeintadd(intx,inty)returnx+y;intadd(intx,inty,intz)returnx+y+z;voidmain()inta=4,b=6,c=10;coutadd(a,b),add(a,b,c)0?x:-x);voidmain()coutabs(-3),abs(-2.6),子对象m(子对象参数m)派生类新增成员的初始化语句;,派生类构造函数执行的一般次序如下:,(1)最先调用基类的构造函数,多个基类则按派生类声明时列出的次序、从左到右调用,而不是初始化列表中的次序。(2)再次调用对象成员(子对象)的构造函数,按类声明中对象成员出现的次序调用,而不是初始化列表中的次序。(3)最后执行派生类的构造函数。,其中,如果派生类新增成员中有某个类的子对象,第(2)步的调用才会执行,否则就直接跳转到第(3)步,执行派生类的构造函数。,派生类的析构函数,和构造函数一样,析构函数也不能被继承,因此在执行派生类的析构函数时,基类的析构函数也将被调用,其顺序与执行构造函数时的顺序正好相反。派生类析构函数执行的一般次序如下:,(1)最先执行派生类的析构函数。(2)再次调用对象成员(子对象)的析造函数,按类声明中对象成员出现的逆序调用,而不是初始化列表中的次序。(3)最后调用基类的析构函数,多个基类则按派生类声明时列出的逆序、从右到左调用,而不是初始化列表中的次序。,基类对象和派生类对象的使用关系,派生类对象作为基类对象处理,由于派生类具有所有基类的成员,所以把派生类的对象赋给基类对象是合理的,不过要求这种继承方式必须是public方式。但是,反过来赋值会使基类中不具有派生类的成员(因为派生类的成员通常比基类的成员多),所以这是不允许的。,基类指针指向派生类对象,因为派生类对象也是基类对象,所以指向派生类对象的指针可以转换为指向基类对象的指针,这种引用方式是安全的,但是用这种方式只能引用基类成员。如果试图通过基类指针引用那些只有在派生类中才有的成员,编译系统会报告错误。,例如,声明一个基类A和派生类B:classA;classB:A;以下语句先定义了基类A的对象指针p和派生类B的对象b,并通过p指向b:A*p;Bb;p=Base()coutdestructingBaseclassendl;classSubs:publicBasepublic:Subs()coutconstructingsubclassendl;Subs()coutdestructingsubclass虚函数(实参)”。,限制虚函数,一个基类中将所有的成员函数都尽可能地设置为虚函数总是有益的(如果该类不是派生类的基类,将成员函数声明为虚函数是没有意义的)。它除了会增加一些资源开销,没有其他坏处。在设置虚函数时必须注意以下几点:,(1)只有类的成员函数才能声明为虚函数。这是因为虚函数仅适用于有继承关系的类对象,所以普通函数不能声明为虚函数。(2)静态成员函数不能是虚函数,因为静态成员函数不受限于某个对象。(3)内联函数不能是虚函数,因为内联函数是不能在运行中动态确定其位置的,即使虚函数在类的内部定义,编译时仍将其看作是非内联的。(4)构造函数不能是虚函数,因为构造时对象还是一片未定型的空间。只有在构造完成后,对象才能成为一个类的名副其实的实例。(5)析构函数可以是虚函数,而且通常声明为虚函数。声明虚析构函数的目的在于:使用delete运算符删除一个对象时,能确保析构函数被正确地执行。这是因为,设置虚析构函数后,可以利用动态绑定方式选择析构函数。,纯虚函数和抽象类,纯虚函数,纯虚函数是一种特殊的虚函数,它是被标明为不具体实现的虚函数,从语法上讲,纯虚函数是在虚函数的后面加上“=0”,表示该虚函数无函数体,这里的“=”并非赋值运算。声明纯虚函数的一般格式如下:virtual类型函数名(参数表)=0;,许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。,抽象类,抽象类是一种特殊的类,它是为了抽象和设计的目的而建立的,它处于继承层次结构的上层。抽象类的主要作用是将有关的类组织在一个继承层次结构中,由它来为它们提供一个公共的根,相关的子类是从这个根派生出来的。,对于抽象类的使用有几点规定:,(1)抽象类只能用作其他类的基类,不能建立抽象类对象。(2)抽象类不能用作参数类型、函数返回类型或显式转换的类型。(3)可以定义指向抽象类的指针和引用,此指针可以指向它的派生类,进而实现多态性。,与抽象类相对应的,将能够建立对象的类称为具体类。,设计统一的公共接口,在类层次结构中,尽可能的为类设计一个统一的公共接口(界面),即采用抽象基类设计方法。一个统一的公共接口必须要经过精心的分析和设计。通常采用如下策略:,(1)分析相关对象的需求,设计出一组实现公共功能的函数。(2)将这些函数作为基类的虚函数(或纯虚函数),它们定义了一个统一的公共接口。(3)由该基类派生出若干子类,在各个子类中实现这些虚函数。,应用实例,编写一个程序实现图书和杂志销售管理。当输入一系列图书和杂志销售记录后,将销售良好(图书每月售500本以上,杂志每月2500本以上)的图书和杂志名字显示出来。,练习题8,定义高度基类High,其数据成员为高h,定义成员函数disp()为虚函数。然后再由High派生出长方体类Cuboid与圆柱体类Cylinder。并在两个派生类中定义成员函数disp()为虚函数。在主函数中,用基类High定义指针变量p,然后用指针p动态调用基类与派生类中虚函数disp(),显示长方体与圆柱体的体积。,上机实验题8,定义图形集类Graphics及其多态绘图函数,依设置顺序输出画笔移动和绘制图形的步骤。,第9章C+流,什么是流,流的概念,数据不断地从设备流向变量(对象),又不断地从变量(对象)流向设备。这种数据的流动就是变量(对象)与外设之间的输入/输出操作,因此,常将数据输入/输出称为输入流/输出流。,流是用流类定义的对象,如cin、cout等。因为流类是用于完成输入/输出操作的类,所以用流类定义的对象就是流对象(简称为流),流向程序员提供输入/输出接口,该接口可使得程序的设计尽可能与所访问的具体设备无关。,C+提供了两种类型的流:文本流(TextStream)和二进制流(BinaryStream)。文本流是一串ASCII字符,而二进制流则是由一串二进制数组成的。源程序文件和文本文件在传送时均采用文本流。,缓冲流与非缓冲流,系统在主存中开辟的用于临时存放输入/输出流信息的内存区称为缓冲区。输入/输出流也相应的分成缓冲流与非缓冲流。,对于非缓冲流,一旦数据送入流中,立即进行处理。而对于缓冲流,只有当缓冲区满时,或当前送入的数据为新的一行字符时,系统才对流中的数据进行处理(称为刷新)。,数据缓冲区示意图,流类库,主要的流类,类ios:它派生出了输入类istream与输出类ostream,是所有基本流类的基类。其他基本流类均由该类派生出来。输入类istream:它专门负责提供输入(提取)操作的成员函数,使输入流对象能通过其成员函数完成数据输入操作任务。输出类ostream:它专门负责提供输出(插入)操作的成员函数,使输出流对象能通过其成员函数完成数据输出操作任务。输入/输出类iostream:它是类istream和ostream公有派生的,该类并没有提供新的成员函数,只是将类istream和ostream组合在一起,以支持一个流对象,既可完成输入操作,又可完成输出操作。ifstream:它从istream类派生的,专门负责把一个文件绑定到程序上用来输入。ofstream:它从ostream类派生,专门负责把一个文件绑定到程序上用来输出。fstream:它从iostream类派生,专门负责把一个文件绑定到程序上用来输入和输出。,标准流,C+使用流类库定义了4个标准流:cin、cout、cerr、clog。以实现数据流的输入与输出操作。在程序开始运行时,C+会自动打开这4个流,这些流是C+流类库的预定义流,如表所示。,输入/输出流,输入流,输入流是用流提取运算符(“”)实现的。流读取运算符通常会跳过输入流中的空格、Tab键、换行符等的空白字符,,1.流提取运算符,流提取运算符(“”)可实现流的输入,包括输入各种内部类型的数据项和字符串等。流提取运算符可以连续使用,如cinxy表示输入x和y的值。,2.成员函数get和getline,cin的成员函数get用于读取单个字符,其语法格式如下:cin.get(),3.istream类中的其他成员函数(peek、putback和ignore),成员函数ignore用于在需要时跳过流中指定数量的字符(默认个数是1),或在遇到指定的分隔符时结束。成员函数putback将最后一次用get从输入流中提取的字符放回到输入流中。成员函数peek返回输入流中的下一个字符,但并不将其从输入流中删除。,输出流,C+的类ostream提供了格式化输出和无格式输出的功能。输出功能包括:用流插入运算符输出标准类型的数据、用成员函数put输出字符、成员函数write的无格式化输出。,1.用流插入运算符输出标准类型的数据,流插入运算符(“”)可实现流的输出,包括输出内部类型的数据项、字符串和指针值等。,2.用成员函数put输出字符,cout的成员函数put的语法格式如下:cout.put(charc)其中,c是要输出的字符。,3.成员函数write的无格式化输出,cout的成员函数write的语法格式如下:cout.write(char*pstr,intn)其中,pstr指向要输出字符串的首地址,n为输出的字符个数。,流的格式控制,在C+中,数据的输入/输出格式控制主要有两种方式,一是使用输入/输出流操纵符,二是使用流格式状态标志。两者可以达到同样的效果。,采用第一种方式,就是直接使用C+提供的大量流操纵符进行输入/输出的格式控制。这里主要介绍第二种方式,分别讲解了格式状态标志、设置流格式状态标志的成员函数、设置流格式状态标志的操纵符、尾数零和十进制小数点(ios:showpoint)、对齐(ios:left、ios:right)、整数流的基数(ios:dec、ios:oct、ios:hex、ios:showbase)、定点格式和科学计数法(ios:scientific、ios:fixed)等功能。,流的错误状态,C+流提供了如下成员函数用于检测流的状态。,inteof():返回非0值表示到达文件尾。intfail():返回非0值表示操作失败。intbad():返回非0值表示出现错误。intgood():返回非0值表示流操作正常。,重载输入/输出运算符,重载输出运算符“”,在C+中,输出操作称为插入,“”称为插入运算符。当重载输出运算符“”,在C+中,“”运算符称为提取运算符,对它进行重载的函数称为提取符函数。这个运算符函数接收流的输入信息。其格式如下:friendistream该提取符函数是以友元方式声明的。其中的第一个参数是istream类对象的一个引用,即stream必须是一个输入流。,文件的操作,文件和流,C+把每一个文件都看成一个有序的字节流,如图所示,每一个文件都以文件结束符EOF结束。,文件的打开与关闭,文件流可以分为3类:输入文件流、输出文件流以及I/O文件流,相应地必须将文件流声明为ifstream、ofstream以及fstream类的对象。例如:ifstreamifile;/声明一个输入流ofstreamo

温馨提示

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

评论

0/150

提交评论