C++程序设计(第2版下)清华出版社ppt.ppt

上传人:za****8 文档编号:15798698 上传时间:2020-09-06 格式:PPT 页数:114 大小:620.02KB
返回 下载 相关 举报
C++程序设计(第2版下)清华出版社ppt.ppt_第1页
第1页 / 共114页
C++程序设计(第2版下)清华出版社ppt.ppt_第2页
第2页 / 共114页
C++程序设计(第2版下)清华出版社ppt.ppt_第3页
第3页 / 共114页
点击查看更多>>
资源描述
C+程序设计(第2版),作者:戴仕明、赵传申等 建议课时:64学时,第11章 继承和派生第12章 多态性和虚函数第13章 运算符重载 第14章 输入输出流与文件操作第15章 模 板 第16章 异 常 处 理,第11章,继承和派生,本章主要内容,11.1 继承和派生的概念 11.2 派生类成员的访问属性 11.3 派生类的构造函数和析构函数 11.4 虚 基 类 11.5 子类型关系,11.1 继承和派生的概念,11.1.1 基本概念 11.1.2 单一继承 11.1.3 多重继承,11.1.1 基本概念,继承是面向对象程序设计中最重要的机制,它支持层次分类的观点。继承使得程序员可以在一个较一般的类的基础上很快地建立一个新类,而不必从零开始设计每个类 在C+中,一个类从另一个类继承特征,称为派生一个类,所派生的类称派生类,另一个类称为基类,类的派生可以无限继续下去。派生类不但继承了基类所有的成员变量和成员函数,而且可以添加新的成员变量和成员函数。 继承常用来表示类属关系,不能将继承理解为构成关系。当从已有的类中派生出新的类时,可以对派生类做以下几种变化: (1)全部或部分地继承基类的成员数据或成员函数。 (2)增加新的成员变量。 (3)增加新的成员函数。 (4)重新定义已有的成员函数。 (5)改变现有的成员属性。,11.1.2 单一继承,在C+中,一个派生类可以从一个基类派生,也可以从多个基类派生,从一个基类派生的继承称为单一继承。图11.1所示为单一继承的示意图。,图11.1 单一继承示意图,在定义单一继承时,派生类只能有一个直接父类,其定义如下: Class派生类名:基类名 private: ./私有成员说明 public: ./公有成员说明 protected: . /保护的成员说明 ; 其中,派生类名为新定义的派生类的名字,基类名是派生类的直接基类的名字,继承方式可以是public(公有)、private(私有)和protected(保护),它用于影响基类成员到派生类后访问权限,继承方式可以省略,默认为private。成员说明表用于给出在派生类中新增的成员。,11.1.3 多重继承,为一个派生类指定多个基类,这样的继承结构被称为多重继承。多重继承形成一个有向无环圆,图11.3所示为多重继承的示意图。,图11.3 多重继承示意图,用多个基类来派生一个类时,其一般格式为: class类名:类名1,类名2,类名n private: /私有成员说明 Public: /公有成员说明 Protected: /保护的成员说明 ; 其中,派生类“类名”继承了类名1n的所有成员数据和成员函数,每一个基类的类名前的Access用以限定该基类中的成员在派生类中的访问权限,其规则与单一继承的用法类同。,11.2 派生类成员的访问属性,11.2.1 公有继承 11.2.2 私有继承 11.2.3 保护成员和保护继承 11.2.4 多级派生时的访问属性,11.2.1 公有继承,在定义一个派生类时将基类的继承方式指定为Publicpublic的,称为公用继承,用公用继承方式建立的派生类称为公用派生类,其基类称为公用基类。 公有派生时,基类中所有成员在公有派生类中保持各个成员访问权限。具体地说,基类中说明为public的成员,在派生类中仍保持为public的成员,在派生类或在派生类外都可以直接使用这些成员。基类中说明为private的成员,属于基类私有的,在公有派生类中不能直接使用基类中的私有成员,必须通过该基类公有的或保护的成员函数来间接使用基类中的私有成员。对于基类中说明为private的成员,可以在公有派生类中直接使用它们,其用法与公有成员一样。但在派生类之外,不可直接访问这种类型的成员,必须通过派生类的公有的或保护的成员函数或者基类的成员函数才能访问它。,11.2.2 私有继承,在声明一个派生类时将基类的继承方式指定为private的,称为私有继承,用私有继承方式建立的派生类称为私有派生类,其基类称为私有基类。 对于私有派生类而言,其基类中公有成员和保护成员在派生类中均变为私有的,在派生类中仍可直接使用这些成员。在派生类之外均不可直接使用基类中的公有或私有成员,这些成员必须通过派生类中的公有成员函数来间接使用它们。同样地,对于基类中的私有成员,在派生类中不可直接使用,只能通过基类的公有或保护成员函数间接使用它们。当然在派生类之外,更是不能直接使用基类中的私有成员。,11.2.3 保护成员和保护继承,在定义一个派生类时将基类的继承方式指定为protected的,称为保护继承,用保护继承方式建立的派生类称为保护派生类,其基类称为受保护的基类,简称保护基类。 保护继承的特点是:保护基类的公用成员和保护成员在派生类中都成了保护成员,其私有成员仍为基类私有。也就是把基类原有的公用成员也保护起来,不让类外任意访问。,11.2.4 多级派生时的访问属性,一个派生类也可以作为基类来派生出另一个新的类。A为基类,类B是类A的派生类,类C是类B的派生类,类D是类C的派生类,则类D也是类A的派生类。类B称为类A的直接派生类,类C、D称为类A的间接派生类。类A是类B的直接基类,是类C、D的间接基类。 无论哪一种继承方式,在派生类中是不能访问基类的私有成员的,私有成员只能被本类的成员函数所访问。 在多级派生时都采用公用继承方式,那么直到最后一级派生类都能访问基类的公用成员和保护成员。 采用私有继承方式,经过若干次派生之后,基类的所有成员已经变成不可访问的了。 采用保护继承方式,在派生类外是无法访问派生类中的任何成员的,而且经过多次派生后,人们很难清楚地记住哪些成员可以访问,哪些成员不能访问,很容易出错。 因此,在实际中,常用的是公用继承。,11.3 派生类的构造函数和析构函数,11.3.1 简单的派生类的构造函数 11.3.2 有成员对象的派生类的构造函数 11.3.3 多级派生和多重继承的构造函数 11.3.4 派生类构造函数的特殊形式 11.3.5 派生类的析构函数 11.3.6 多重继承的二义性问题,11.3.1 简单的派生类的构造函数,任何派生类都包含基类的成员,只有一个基类,而且只有一级派生,在派生类的数据成员中不包含基类的对象的派生类即为简单的派生类。 派生类的构造函数的一般语法格式为: 派生类构造函数名(总参数表列):基类构造函数名(参数表列) 派生类中新增数据成员初始化语句,11.3.2 有成员对象的派生类的构造函数,类的数据成员中除了一般的数据类型int、float、char,还可以包含类对象,称为成员对象或子对象,即对象中的对象。 有成员对象的派生类构造函数的一般语法格式为: ():(),(). ; 这里要注意的是,和中的参数都是来自于。 执行派生类构造函数的顺序是: (1)调用基类构造函数,对基类数据成员初始化。 (2)调用成员对象构造函数,对成员对象数据成员初始化。 (3)再执行派生类构造函数本身,对派生类数据成员初始化。,11.3.3 多级派生和多重继承的构造函数,多重继承的派生类构造函数的一般语法格式为: ():(),(). ; 多级派生时的构造函数语法格式如下: ():() ; 多级派生不必把每一级基类都写出来。,11.3.4 派生类构造函数的特殊形式,在使用派生类构造函数时,需要注意有以下特殊的形式: 1) 派生类构造函数的函数体为空 2) 派生类构造函数中不写基类构造函数 3) 派生类的默认构造函数 4) 必须显式地定义派生类构造函数 5) 构造函数的调用顺序,11.3.5 派生类的析构函数,与派生类的构造函数一样,派生类无法继承基类的析构函数,所以要定义派生类的析构函数,通过派生类的析构函数去调用基类的析构函数。在派生类中可以根据需要定义自己的析构函数,用来对派生类中所增加的成员进行清理工作。基类的清理工作仍然由基类的析构函数负责。在执行派生类的析构函数时,系统会自动调用基类的析构函数和成员对象的析构函数。 调用析构函数的顺序与构造函数的顺序相反。 执行派生类析构函数的顺序是: (1)先调用派生类析构函数,对派生类清理; (2)再调用成员对象析构函数,对成员对象清理; (3)最后调用基类析构函数,对基类清理。,11.3.6 多重继承的二义性问题,在多重继承中,当多个基类中包含同名的成员时,它们在派生类中就会出现成员名二义性问题。 产生二义性问题主要有两种情况。 第一种情况是一个类由多个基类派生时,不同基类中的数据成员或成员函数同名,在派生类中使用不同基类中的同名成员时产生二义性。 第二种情况是当一个派生类由多个基类派生,而这些基类又有一个共同的基类,当对该基类中说明的成员进行访问时,可能出现二义性。 下面介绍解决二义性问题的主要的四种方法: (1)通过作用域运算符(:)明确指出访问的是哪一个基类中的成员。使用作用域运算符进行限定的一般格式是: .: /数据成员 .:() /成员函数,(2)在类中定义同名成员。与基类成员同名的派生类成员将屏蔽对基类成员的直接访问,这就是支配原则。 (3)虚基类。虚基类保证了一个类不能从同一个类中直接继承一次以上。 (4)各基类中的成员各不相同,当然,这不是一个很好的解决办法。 另外,二义性检查在访问控制权限或类型检查之前进行,所以访问控制权限不同或类型不同不能解决二义性问题,即在两个基类中,成员名相同,即使一个为private,一个为public也是不能避免二义性的。,11.4 虚 基 类,虚基类是对派生类而言,所以,虚基类本身的定义同基类一样,在定义派生类时声明该基类为虚基类即可,就是冠以关键字virtual。 虚基类的语法格式为: class : virtual . 关键字virtual告诉编译器,该基类不管经过多少次派生,在其派生类中只能有该基类的一个拷贝。在引用这些同名的成员时,必须在成员前加上基类的类名,以避免二义性。,11.5 子类型关系,有一个特定的类型S,当且仅当它至少提供了类型T的行为,则称类型S是类型T的子类型。 只有public继承可以实现子类型,只有public派生类才是基类真正的子类型,它完整地继承了基类的功能。称派生类为基类的子类型,它是基类的特殊化。称基类为父类型,它是所有派生类的一般化。 子类型关系使得在需要基类对象的任何地方都可以使用公有派生类的对象来替代,从而可以使用相同的函数统一处理基类对象和公有派生类对象(形参为基类对象时,实参可以是派生类对象),而不必为每一个类设计单独的处理程序,大大提高了程序的效率。它是实现多态性的重要基础之一。 在公有继承方式下,可以实现子类型关系,即派生类A是基类B的子类型。 子类型的重要性就在于减轻程序人员编写程序代码的负担。,第12章,多态性和虚函数,本章主要内容,12.1 多态性的概念 12.2 虚 函 数 12.3 纯虚函数和抽象类,12.1 多态性的概念,多态性是人类处理问题过程中一种普遍的规则。处理问题要求“原则性”与“灵活性”。 在程序中,也需要多态性。这种多态性可以简单地概括为“一个接口,多种方法”。 多态性通过匹配实现。匹配是把函数调用与适当的函数码相对应的活动。 在一个层次类的不同类中,可以用同一个名字命名语义相似的方法函数,而在各自的类中给予不同的定义,以形成不同的版本。 在C+中,动态多态性通过虚函数机制来实现。,12.2 虚 函 数,12.2.1 虚函数的作用 12.2.2 静态关联和动态关联 12.2.3 虚函数的使用 12.2.4 虚析构函数,12.2.1 虚函数的作用,在同一类中是不能定义两个名字相同、参数个数和类型都相同的函数的,否则就是“重复定义”。但是在类的继承层次结构中,在不同的层次中可以出现名字相同、参数个数和类型都相同而功能不同的函数。 虚函数可以让成员函数操作一般化,用基类的指针指向不同的派生类的对象时,基类指针调用其虚成员函数,则会调用其真正指向对象的成员函数,而不是基类中定义的成员函数(只要派生类改写了该成员函数)。若不是虚函数,则不管基类指针指向的哪个派生类对象,调用时都会调用基类中定义的那个函数。虚函数用于实现多态性。 虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。,12.2.2 静态关联和动态关联,确定调用的函数所对应的具体对象的过程称为关联或绑定(Binding)。 在这里是指把一个函数名与一个类对象捆绑在一起,建立关联。前面提到的函数重载和通过对象名调用的函数,在编译时刻即可确定其调用的函数属于哪个类,其过程称为静态关联或静态绑定(Static Binding), 函数重载和通过对象名调用的虚函数,在编译时即可确定其调用的虚函数属于哪一个类,其过程称为静态关联。 在运行阶段,基类指针变量先指向了某一个类对象,然后通过此指针变量调用该对象中的函数。把虚函数和类对象“绑定”在一起的,此过程称为动态关联或动态绑定(Dynamic Binding)。 在运行阶段,指针可以先后指向不同的类对象(引用可以先后引用不同的类对象),从而调用同一个类层次结构中的不同类对象的虚函数。由于动态绑定是在编译以后的运行阶段进行的,因此也称为滞后绑定(Late Binding)。,12.2.3 虚函数的使用,关于虚函数,须说明以下几点。 (1)virtual 只用在类定义的虚函数说明中,若虚函数在类体外实现,则不能在实现前加vritual。 (2)virtual具有继承性。 (3)实现这种动态的多态性时,必须使用基类类型的指针变量,使该指针指向不同派生类的对象,并通过调用指针所指向的虚函数才能实现动态的多态性。 (4)虚函数必须是类的一个成员函数,不能是友员函数,也不能是静态的成员函数。 (5)在派生类中没有重新定义虚函数时,与一般的成员函数一样,当调用这种派生类对象的虚函数时,则调用其基类中的虚函数。 (6)构造函数不能是虚函数,可把析构函数定义为虚函数。通常在释放基类中和其派生类中的动态申请的存储空间时,也要把析构函数定义为虚函数,以便实现撤销对象时的多态性。 (7)虚函数与一般的成员函数相比较,调用时的执行速度要慢一些。,12.2.4 虚析构函数,析构函数的作用是在对象撤销之前做必要的“清理现场”的工作。当派生类的对象从内存中撤销时一般先调用派生类的析构函数,然后再调用基类的析构函数。但是,如果用new 运算符建立了临时对象,若基类中有析构函数,并且定义了一个指向该基类的指针变量,在程序用带指针参数的delete 运算符撤销对象时,会发生一个情况:系统会只执行基类的析构函数,而不执行派生类的析构函数。 此时可以将基类的析构函数声明为虚函数时,由该基类所派生的所有派生类的析构函数也都自动成为虚函数,即使派生类的析构函数与基类的析构函数名字不相同。,12.3 纯虚函数和抽象类,12.3.1 纯虚函数 12.3.2 抽象类 12.3.3 应用实例,12.3.1 纯虚函数,定义纯虚函数的一般语法格式为: virtualFuncName()=0; 要注意以下几点。 (1)纯虚函数没有函数体,不能定义虚函数的实现部分。 (2)最后面的“=0”并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是纯虚函数”。 (3)这是一个声明语句,最后应有分号。 (4)如果在基类中没有保留函数名字,则无法实现多态性。 (5)把至少包含一个纯虚函数的类称为抽象类。,12.3.2 抽象类,在面向对象程序设计中,往往有一些类,它们不用来生成对象,定义这些类的唯一目的是用它作为基类去建立派生类。 把至少包含一个纯虚函数的类称为抽象类。这种类只能作为派生类的基类,不能用来说明这种类的对象。其理由很明显:因为虚函数没有实现部分,所以不能产生对象。 如果在抽象类所派生出的新类中对基类的所有纯虚函数进行了定义,那么这些函数就被赋予了功能,可以被调用,这个派生类就不是抽象类。 如果在派生类中没有对所有纯虚函数进行定义,则此派生类仍然是抽象类,不能用来定义对象。,12.3.3 应用实例,(1)一个基类如果包含一个或一个以上纯虚函数,就是抽象基类。抽象基类不能也不必要定义对象。 (2)抽象基类与普通基类不同,它一般并不是现实存在的对象的抽象,它可以没有任何物理上的或其他实际意义方面的含义。 (3)如果在基类声明了虚函数,则在派生类中凡是与该函数有相同的函数名、函数类型、参数个数和类型的函数,均为虚函数(不论在派生类中是否用virtual 声明)。 当继承一个抽象类时,必须实现所有的纯虚函数,否则继承的类仍然是抽象类,同时,继承的类还要对纯虚函数重新定义,提供该函数的实现部分。 纯虚函数是非常有用的,因为它使得类有明显的抽象性,纯虚函数和抽象类可以用来实现动态的多态性。,第13章,运算符重载,本章主要内容,13.1 运算符重载 13.2 重载运算符 13.3 重载流插入和流提取运算符 13.4 几种特殊运算符的重载 13.5 不同类型数据间的转换 13.6 字 符 串 类,13.1 运算符重载,13.1.1 运算符重载的概念 13.1.2 运算符重载的方法和规则,13.1.1 运算符重载的概念,同一个运算符“+”可用于完成实数加法、复数加法及字符串连接等不同的运算,得到完全不同的结果。这就是“+”运算符的重载。 所谓运算符重载就是用同一个运算符完成不同的运算操作。在C+这类面向对象的程序设计语言中,运算符重载可以完成两个对象的复杂操作(如两个复数的算术运算等)。而运算符重载是通过运算符重载函数来完成的。 采用面向对象的程序设计时,C+允许程序设计者重新定义已有的运算符,并能按设计者规定要求去完成特定的操作,这就是运算符的重载。其本质是通过调用一个函数来实现运算符的功能。,13.1.2 运算符重载的方法和规则,运算符重载的方法是定义一个重载运算符的函数,在需要执行被重载的运算符时,系统就自动调用该函数,以实现相应的运算。运算符重载实质上是函数的重载。 定义运算符重载函数的一般语法格式为: ClassName:operator() . /函数体 其中,type为函数返回值的类型;ClassName为运算符重载函数所在的类名;为要重载的运算符;为函数的形参表;operator是关键字,它与其后的一个运算符一起构成函数名。 运算符被重载后,其原有的功能仍然保留,没有丧失或改变。 虽然在重载运算符时,可以像重载普通函数那样具有很大的灵活性,但也要遵循一定的规则和原则,这样才能正确重载运算符。总结如下: (1)运算符重载函数的函数名必须为operator,后跟一个合法的运算符。 (2)用成员函数实现运算符重载时,只能有一个参数或没有参数。,(3)不能改变运算符的操作数个数。 (4)不能改变运算符的优先级。 (5)运算符重载不改变运算符的结合性。 (6)应该使重载的运算符函数的功能类似于该运算符作用于基本数据类型时所实现的功能。 (7)只能对已有运算符重载,不能创建新的运算符。 (8)重载的运算符函数可以是类的成员函数,也可以是全局(友元)函数,但是有时必须采用友元的方式重载。,13.2 重载运算符,13.2.1 重载双目运算符 13.2.2 重载单目运算符,13.2.1 重载双目运算符,双目运算符有两个操作数,通常在运算符的左右两侧,在重载双目运算符时,不言而喻在函数中应该有两个参数。 运算符函数operator+被定义为公有的,以便程序的其他函数能使用它,在定义了函数之后,就可以像在内定义类型上一样,对复数对象用+表达式实施运算。C+编译器把表达式a+b解释为函数调用a.operator+(b)。在调用它时,operator+成员函数首先创建一个临时的complex对象temp,然后把出现在加法表达式中的两个复数之和暂存其内,最后将这个临时对象返回。 运算符重载函数也可以为友元函数,其一般定义格式为: operator() . /函数体 其中,type为函数返回值的类型;为要重载的运算符;为函数的形参表;operator是关键字,它与其后的一个运算符一起构成函数名。对于形参需说明如下:,当重载函数为友元普通函数时,该重载函数不能用对象调用,因此参加运算的两个对象必须以形参方式传送到重载函数体内,所以运算符重载函数为友元函数时,形参通常为两个参加运算的对象。 友元函数与成员函数作为双目运算符重载函数的另一个区别是:当重载函数为成员函数时,双目运算符的左操作数为调用重载函数的对象。右操作数为实参。当重载函数为友元函数时,双目运算符的左操作数为调用重载函数的第一个实参,右操作数为第二个实参。,13.2.2 重载单目运算符,由于单目运算符只有一个操作数,因此运算符重载函数只有一个参数,如果运算符重载函数作为成员函数,则还可省略此参数。 用成员函数重载单目运算符时,其一般语法格式为: ClassName:operator ( ) . /函数体 对于+和-单目运算符,重载时必须区分前置和后置运算,以便编译器根据这种区分来调用不同的运算符重载函数。 其中,type是函数返回值的类型,ClassName为类名。 (1)“前置+”运算符重载成员函数的说明。在主函数中执行b=+a语句时,先将a自加,然后将a赋给b。 (2)“后置+”运算符重载成员函数的说明。在主函数中执行c=a+语句时,先将a赋给c,然后将a自加。 (3)用成员函数实现单目运算符的重载时,运算符的左操作数或右操作数为调用重载函数的对象。,13.3 重载流插入和流提取运算符,13.3.1 重载流插入运算符“”,13.3.1 重载流插入运算符“”,运算符“” 必须被重载为类的友元函数,重载语法格式为: ostream ,13.3.2 重载流提取运算符“”,cin是类istream的一个对象,用于管理数据输入,“”称为提取运算符。它也必须被重载为类的友元函数。重载语法格式为: istream a=6.8+a; 输出结果:a=11 在C+中,6.8作为双精度型,先把5转换为双精度型,求得结果11.8,然后转换成11赋值给整型的对象i,这种转换是自动进行的,称为隐式类型转换。 C +还提供显式类型转换,程序人员在程序中指定将一种指定的数据转换成另一种指定的类型,其形式为: 类型名(数据) 如int (76.5) ,其作用是将76.5 转换为整型数76。这就是接触的标准类型之间的转换。,13.5.2 转换构造函数,在理解转换构造函数之前,先回顾一下以前学习过的几种构造函数。 (1)默认构造函数。以complex 类为例,函数原型的形式为: complex ( ); /没有参数 (2)用于初始化的构造函数。函数原型的形式为: complex (double r , double i);/形参表列中一般有两个以上参数 (3)用于复制对象的复制构造函数。函数原型的形式为: complex ( ComPlex 其作用是将double 型的参数r转换成Complex类的对象,将r作为复数的实部和虚部。用户可以根据需要定义转换构造函数,在函数体中告诉编译系统怎样去进行转换。在类体中,可以有转换构造函数,也可以没有转换构造函数,视需要而定。以上几种构造函数可以同时出现在同一个类中,它们是构造函数的重载。编译系统会根据建立对象时给出的实参的个数与类型,选择形参与之匹配的构造函数。 带有参数的构造函数可以用作从一个基本数据类型转换到一个类。,13.5.3 类型转换函数,类型转换函数的作用是将一个类型的对象转换成另一个类型的数据。 类型转换函数是类中定义的一个成员函数,作用是将对象内的成员数据转换成type类型的数据,其一般语法格式为: ClassName:operator() . /函数体 其中,ClassName是类名;type是要转换后的一种数据类型;operator与type一起构成转换函数名。该函数不能带有参数,也不能指定返回值类型,它的返回值的类型是type。 需要说明的是:转换函数只能是成员函数,其操作数是this指针所指向的对象。在一个类中可以定义多个转换函数。转换函数可以被派生类继承,也可以定义为虚函数。,13.6 字 符 串 类,C+系统提供的字符串处理能力比较弱,字符串复制、连接、比较等操作不能直接通过“=”、“+”、“”等运算操作符完成,而必须通过字符处理函数来完成。 通过C+提供的运算符重载机制,可以提供对字符串直接操作的能力,使得对字符串的操作与对一般数据的操作一样方便。 在定义的字符串类中,重载运算符“=”实现字符串的赋值;重载运算符“+”实现两个字符串的拼接;重载运算符“”、“=”分别实现字符串之间的直接比较;也定义了转换函数等,以便提供使用上的方便。,第14章,输入输出流与文件操作,本章主要内容,14.1 流 的 概 念 14.2 基本I/O流类体系概述 14.3 流的格式控制与标准输入/输出 14.4 文件流和文件操作,14.1 流 的 概 念,1. 流、流类、流类库 C+的输入输出流是指由若干字节组成的字节序列,这些字节中的数据按顺序从一个对象传送到另一对象。流表示了信息从源到目的端的流动。 流中的内容可以是ASCII字符、二进制格式的数据、图形图像、数字音频视频或其他形式的信息。输入/输出的任务实际上就是以一种稳定、可靠的方式在任何设备与内存之间传输数据流。从内存输出到设备的数据流称为输出流(Output stream);从设备输入到内存的数据流称为输入流(Input stream)。这种数据的流动就是对象与外设之间的输入/输出(I/O)操作,如图14.1所示。,图14.1 对象与外设之间的数据流动,2. 流的分类 数据的输入/输出以字节为单位,依次逐个进行。数据的字节序列称为字节流。按对字节内容的解释方式分,字节流分为字符流(也称文本流,Text Stream)和二进制流(Binary Stream)。 文本流是一串ASCII字符,将字节流的每个字节按ASCII字符解释。 而二进制流则是由一串二进制数组成,将字节流的每个字节按二进制方式解释。 3. 缓冲流和非缓冲流 在缓冲区中是否立即处理,流分为缓冲流和非缓冲流。对于非缓冲流,一旦数据送入流中,立即进行处理。而对于缓冲流,只有当缓冲区满时,或当前送入的数据为新的一行字符时,系统才对流中的数据进行处理(称为刷新)。,14.2 基本I/O流类体系概述,14.2.1 基本I/O流类体系 14.2.2 预定义的标准输入输出流,14.2.1 基本I/O流类体系,在头文件“iostream.h”中定义了C+的基本I/O流类体系,其结构如图14.3所示。C+的基本流类体系是由基类ios、输入类istream、输出类ostream与输入输出类iostream类等组成。,图14.3 基本I/O流类体系,1. 基类ios 基类ios派生出了输入类istream与输出类ostream,是所有基本流类的基类。 2. 输入类istream 输入类istream专门负责提供输入(提取)操作的成员函数,使输入流对象能通过其成员函数完成数据输入操作任务。 3. 输出类ostream 输出类ostream专门负责提供输出(插入)操作的成员函数,使输出流对象能通过其成员函数完成数据输出操作任务。 4. 输入输出类iostream 类iostream是由类istream和ostream公有派生的,该类并没有提供新的成员函数,只是将类istream和ostream组合在一起,以支持一个流对象,既可完成输入操作,又可完成输出操作。,14.2.2 预定义的标准输入输出流,在流类定义的头文件中不仅定义了有关的类,定义了四个标准流对象:cin、cout、cerr和clog。 1. 标准输入流 在C+流类体系中定义的标准输入流是cin,cin是由输入类istream的派生类istream_withassign定义的对象,在默认的情况下,cin所联系的设备文件为键盘,实现从键盘上输入到内存数据流。 2. 标准输出流 在C+流类体系中定义的标准输出流是cout、cerr、clog,其中流cerr和clog为标准错误信息或提示信息输出流。,3.标准输出流的默认设置 标准输出流的默认输出格式如下。 (1)输出整数时:数制为十进制、域宽为0、数字右对齐、以空格填充。 (2)输出实数时:数制为十进制、域宽为0、数字右对齐、以空格填充、精度6位小数、浮点输出。 (3)输出字符串时:域宽为0、数字右对齐、以空格填充。,14.3 流的格式控制与标准输入/输出,14.3.1 流的格式控制 14.3.2 标准输出的控制格式 14.3.3 标准输入的控制格式,14.3.1 流的格式控制,使用流的格式控制可以按指定的格式输入输出数据。数据输入输出的格式控制可通过两种途径实现,一种是为熟悉C+类的用户提供的由ios类定义的格式控制成员函数。另一种是为不熟悉C+类的用户直接提供的预定义格式控制函数。所以格式控制函数分为两类: 一类由ios类定义的格式控制成员函数,必须作为输入输出数据流对象(如:cout、cin)的成员函数来使用,即其使用格式为:流对象. 格式控制成员函数(实参)。 另一类由C+流类库直接提供的操纵符(manipulator,也叫操作符),也称预定义的控制器函数,是为了简化对I/O流的格式设置和简化流的常用操作而定义的一些inline函数,通过调用这些函数可以设置对应流的输出格式。,14.3.2 标准输出的控制格式,1. 整数输出 通过操纵符hex、oct、dec来实现输出的基数的控制。 2. 浮点数输出 浮点数字的输出可以采用定点格式和科学计数法两种,采用操纵符fixed、scientific来控制,同时可以通过setprecision来设置浮点数的精度。 3. 布尔类型输出 布尔类型的数据输出可以采用两种方式:字符串(输出true或false)、数字(输出表示真的1或表示假的0)。分别采用boolalpha和noboolalpha操纵符来选择这两种方式。 4. 输出宽度及填充字符 有时在发票上,了使打印机输出的内容和发票的样式能够对应上,需要使输出项的输出宽度不小于某个宽度,若输出项自身的宽度大于指定的宽度,则使用输出项自身的宽度。输出宽度的设置可以通过操作符setw。,5. 用流成员函数put 输出字符 C+中,除了使用操作符实现输出功能以外,也可以使用输出流对象的put函数, 实现输出单个字符数据。put成员函数既可用于ASCII文本流,也可用于二进制流,尤其适用于二进制流输出。,14.3.3 标准输入的控制格式,1. 整数输入 整数的输入可以采用十进制、八进制、十六进制三种格式。 2. 浮点数输入 使用“”运算符输入浮点数数据时,系统跳过开头的空白字符,然后读取数字或小数点等浮点数的组成字符,直至碰到不合适的字符为止。浮点数的输入可以采用定点数和科学计数法两种格式。 3. 布尔类型的输入 布尔类型的输入可以采用两种格式:字符方式(true和false)、数字方式(1和0)。 4. 字符串的输入 字符串输入与其他类型数据的输入有些不同,因为字符串没有特别的格式说明。而输入流中的所有字符,除空白字符外,都可认为是串的部分。因此能够终止字符串抽取的唯一分隔符是空白字符。与此相反,其他类型数据的抽取一旦遇到不属于那种数据类型的字符就停止。,当利用抽取符“”从输入流抽取字符串时,有两种终止方式: (1)遇到空白字符终止,可通过空格键结束字符串。 (2)使用操纵符setw,通过限定输入字符串长度而截取字符串。如果使用setw时,只能抽取固定数目的字符。 5. 单字符输入函数 除了提取符“”外,可以利用istream类的成员函数get实现单个字符的输入。 6. 字符串输入函数 函数get的另一种格式带有3个参数可以用于字符串的输入,此外还有函数getline用于读取一行字符串。 (1)用get()函数提取字符或字符串时,要单独提取换行符。 (2)用getline()函数提取字符串时,当实际提取的字符个数小于第二个参数指定的字符个数时,则将输入流中表示字符串结束字符也提取出来,但结果不包含该字符。,14.4 文件流和文件操作,14.4.1 文件的概念 14.4.2 文件流类体系和文件流 14.4.3 文件的打开与关闭 14.4.4 文本文件的使用 14.4.5 二进制文件的使用,14.4.1 文件的概念,1.从用户的角度看,文件可分为普通文件和设备文件 普通文件是指驻留在磁盘或其他外部介质上的一个有序数据集,可以是源文件、目标文件、可执行程序;也可以是一组待输入处理的原始数据,或者是一组输出的结果。 设备文件是指与主机相联的各种外部设备。 2.从文件编码的方式看,文件可分为ASCII码文件和二进制码文件 ASCII文件也称为文本(text)文件,这种文件在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。 二进制文件又称内部格式文件或字节文件,是把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放。,14.4.2 文件流类体系和文件流,文件流类体系是从C+的流类体系中派生出来的。当程序中使用文件时,要包含头文件“fstream.h”。 1. filebuf类 在文件流类体系中,类filebuf管理文件的缓冲区,应用程序一般不涉及该类。 2. ofstream类 ofstream类由ostream类公有派生而来,它实现数据写入到文件中的各种操作。 3. ifstream类 ifstream类由istream类公有派生而来,它实现从文件中读数据的各种操作。 4. fstream 类 fstream类由iostream类公有派生而来,它提供了对文件数据的读与写入操作。,文件流是以外存文件为输入输出对象的数据流。输出文件流是从内存流向外存文件的数据,输入文件流是从外存文件流向内存的数据。 对文件的进行输入输出,需要先定义一个文件流类的对象,通过文件流对象将数据输入文件或从文件输出。,14.4.3 文件的打开与关闭,C+中对文件的操作包括定义文件流对象、打开文件、读写文件和关闭文件四步。 (1)定义文件流对象就是创建流对象,通过文件流类ofstream、ifstream或fstream定义出文件流对象。 (2)打开文件就是建立流对象与指定的文件之间的联系。 (3)读写文件就是利用流对象成员函数以及“”和“”运算符、get、getlin、put成员函数进行数据的输入/输出。 (4)关闭文件是把暂存在内存缓冲区中的内容写入到和流关联的文件中,归还打开流对象时申请的资源,并将流对象和文件脱离关系。 1.定义文件流对象 文件的使用有读文件、写文件和读/写文件三种方式。根据三种使用方式不同,应用文件流类ifstream、ofstream、fstream定义三种文件流对象。即:读文件流对象、写文件流对象和读/写文件流对象。,2. 打开文件 打开文件有二种方式,一种是用文件流成员函数open打开文件,另一种是在定义文件流对象时通过构造函数打开文件。 1)使用成员函数open打开文件 在文件流类体系中说明了以下三个打开文件的成员函数,它们分别对应于输入文件流、输出文件流和输入/输出文件流。 2)通过构造函数打开文件 输入、输出、输入/输出三个文件流类中都重载了带有默认参数的构造函数: ifstream: ifstream ( const char * ,int =ios:in,int =filebuf:openprot); ofstream: ofstream ( const char * ,int =ios:out,int =filebuf:openprot); fstream: fstream ( const char * ,int ,int =filebuf:openprot);,由构造函数的原型可知,它们所带的参数与对应的成员函数open()所带的参数完全相同。在创建流对象的同时,系统内部已经打开流,建立流和文件的关联。所以通过调用各自的构造函数,也能打开文件。 3. 文件的读/写 文件打开后,对文件的读/写操作也有两种方法,第一种方法是使用提取运算符()或插入运算符()对文件进行读写操作;第二种方法是使用成员函数进行文件的读写操作。 4. 关闭文件 打开一个文件且对文件进行读或写操作做完后,应该调用文件流的成员函数来关闭相应的文件。,14.4.4 文本文件的使用,三个文件流类ifstream、ofstream、fstream没有直接定义文件操作的成员函数,对文件的操作是通过调用其基类ios、istream、ostream中说明的成员函数来实现的。,14.4.5 二进制文件的使用,二进制文件不是以ASCll 代码存放数据的,文件中的信息不是字符数据,而是字节中的二进制形式的信息,因此它又称为字节文件。 二进制文件除了可以作为输入文件或输出文件外,还可以是既能输入又能输出的文件。 1. 二进制文件读函数read() 二进制文件的读操作是通过成员函数read()来实现。在流类istream中重载了这个函数: istream 函数的作用是:将第二个参数指定字节数的内容读到第一个字符型参数所指存储单元中。,2. 二进制文件写函数write() 二进制文件的写操作是通过成员函数write()来实现。在流类ostream中重载了这个函数: ostream 当到达文件的结束位置时,该函数返回1;否则返回值为0。 4. 随机访问文件的函数 对于二进制数据文件来说,可以利用文件流成员函数移动指针,随机地访问文件中任一位置上的数据,还可以修改文件中的内容。,第15章,模 板,本章主要内容,15.1 模板的概念 15.2 函 数 模 板 15.3 类 模 板,15.1 模板的概念,在程序设计中,一个程序实体的这种能对多种类型的数据进行操作或描述的特性称为泛型(或类属)。基于泛型思想进行的程序设计称为泛型程序设计(Generic Programming)。在泛型程序中,类型成为了类似函数中的形参一样的未知量,而在程序运行时,提供实际的类型。C+中提供了模板特性来支持泛型程序设计。 所谓模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了真正的代码可重用性。C+是一种“强类型”的语言,也就是说一个变量,编译器必须确切的知道它的类型,而模板就是构建在这个强类型语言基础上的泛型系统。由模板可以得到一系列的相似类或相似函数,这些相似类和相似函数涉及的数据其类型可能不同,但处理数据却具有相同的表现形态。C+中的模板包括函数模板和类模板。,15.2 函 数 模 板,15.2.1 函数模板的定义 15.2.2 函数模板的使用和实例化 15.2.3 函数模板的重载 15.2.4 函数模板与宏和函数重载的比较,15.2.1 函数模板的定义,函数模板是指带有类型参数的函数,语法如下: template () . 其中,template是定义模板的关键字,所有函数模板的定义都以关键字template开始。T1、T2等是函数模板的模板参数,必须用尖括号括起来,在调用该函数时将指定T1、T2对应的实际类型,可以是基本数据类型,也可以是类类型。每个模板参数都必须加上前缀class 或者typename。在、和函数体内都可以使用T1、T2等作为类型,只是此时不知道是何具体类型。,15.2.2 函数模板的使用和实例化,使用函数模板,就是以函数模板名为函数名的函数调用,并用尖括号指定相应的具体的类型。其语法格式为: 函数模板名,(实参列表) 这里的、等将分别替换函数模板定义中的模板参数T1、T2,。 使用函数模板时,会触发编译器产生一个对应函数模板的函数体定义。当编译器发现有一个函数模板名为函数名的调用时,先确定类型参数所对应的具体类型,并按该类型生成一个具体函数,然后再调用该函数。该具体函数的定义体与函数模板的定义体相同,而数据形参的类型则以数据实参的类型为依据。 称该具体函数称为模板函数,它是函数模板的一个实例。这个确定函数模板实例的过程叫模板实例化。,15.2.3 函数模板的重载,多个同名的函数模板以及非模板函数之间可以重载,编译器在调用这样的函数时,采用与重载函数解析类似的方式进行调用。 (1)根据函数调用语句中的实参,按照重载函数的匹配机制,编译器寻找和使用最符合函数名和参数类型的函数调用,优先匹配一般函数(非模板函数)。 (2)如果没有找到一般函数,则根据显式指定的或实参推导出的模板参数,实例化函数模板,然后调用实例化的模板函数。 (3)如果不能完全匹配,则检查是否可以通过类型转换,按照步骤(1)和(2)进行匹配。如果匹配,就调用它。如果不匹配,则函数调用失败。,15.2.4 函数模板与宏和函数重载的比较,函数模板定义了一系列的模板函数都是同名的。编译器采用重载的解决方法调用相应函数。模板函数虽然类似于重载函数,但它要更严格一些。函数被重载时,在每个函数体内可以执行不同的动作,但同一函数模板实例化后的所有模板函数都必须执行相同的动作。对上面的例子用函数重载的方法也能够实现。但当需要比较的数据类型越来越多时,重载函数的数量将增加,重要的是这些函数的算法完全相同,只是参数的类型不同。使用模板来处理则简单很多。,15.3 类 模 板,15.3.1 类模板的定义 15.3.2 类模板的实例化,15.3.1 类模板的定义,模板类与通常的类定义没有什么不同,格式如下: template class ; 其中,template是关键字,表示定义的是一个模板。类模板的定义也以template开始。T1、T2等为类模板的类型参数,用尖括号括起来,多个类型参数项之间用逗号隔开,每个类型参数都必须加上前缀class 或者typename。在类成员的说明中,可以使用T1、T2等指定类中数据成员的类型、成员函数的参数类型和返回值类型。 在类模板的任何地方都可以使用类模板的类型参数。对于类模板的成员,若在类体外定义时,必须将成员函数定义为函数模板的形式。,15.3.2 类模板的实例化,在使用类模板时,需要用实际的数据类型代替类型参数,把类模板实例化。其语法格式为: 类模板名, , ,; 的、等将分别替换类模板定义中的类型参数T1、T2,。类型名用尖括号括起来,他们之间用用逗号分隔开。这里的类型名排列顺序与类模板定义时类型参数表中的参数顺序必须一样。 使用时,当编译器遇到对类模板,根据类型参数列表中所给出的类型去替换类模板定义中的相应参数,生成一个具体的类。这个类就是类模板的一个实例,类模板只有被实例化后才能定义对象。将这个产生具体类的过程称为类模板实例化。,定义类对象时,要同时指定类型参数和非类型参数。若在类模板中定义了静态成员,则该静态成员仅属于实例化后的模板类,同一个类模板实例化的不同模板类之间不共享类模板中的静态成员。 在使用类模板时,必须显示提供模板参数,若参数很多,则每次书写比较麻烦,可以使用typedef来定义一个别名 类模板描述多个具有相同结构,仅成员类型不同的类提供了方便,C+提供的标准库中使用类模板实现了大量的容器类模板。,第16章,异 常 处 理,本章主要内容,16.1 异常的概念 16.2 C+的异常处理机制 16.3 异 常 对 象 16.4 异常的重新抛出与嵌套处理 16.5 异 常 规 范,16.1 异常的概念,总的来说,程序的错误可以分为两种:编译错误和运行错误。前者又称语法错误,后者发生在程序运行过程中,分为不可预料的逻辑错误和可以预料的运行异常。 语法错误是指程序中使用了错误的语法、函数、结构和类,造成程序无非编译通过。 逻辑错误又称语义错误,是由于程序设计不当而造成程序不能完成预期的功能。 运行异常是指由程序运行环境问题造成的程序异常中止。,16.2 C+的异常处理机制,16.2.1 抛出异常 16.2.2 try 块 16.2.3 捕获和处理异常 16.2.4 异常处理的执行过程,16.2.1 抛出异常,抛出异常的语法格式如下: throw; 关键字throw表示要抛出一个异常事件。如果某段程序中发现了自己不能处理的异常情况,就可以用该方式抛出这个异常,将它抛给调用该段程序的函数,并将表达式的值传给处理该类型异常事件的处理程序。其中,表达式值的类型称为异常类型,可以是除void之外任意的C+的类型,包括C+的类。 throw 1; /抛出一个异常,该异常为int类型,值为1 throw number error; /抛出一个异常,该异常为char*类型,值为字符串的首地址。 当程序执行完throw语句后,跟在该语句后面的语句将被跳过。系统将直接跳到异常处理部分catch 子句进行异常的处理。如果没有catch 子句能够处理该异常,则程序执行权将被转交给C+运行系统。,16.2.2 try 块,try块的语法格式为: try try 块以关键字try 开始,后面是花括号括起来的复合语句。try 块把语句分成组,并将其与相应地处理这些语句可能抛出的异常的处理语句相关联。当发生异常时,try块中的throw语句会抛出这个异常。 try 块可以包含任何C+语句,包括表达式和声明。一个try 块引入一个局部作用域,在try块内声明的变量不能在try 块外被引用,包括在catch 子句中。也可以声明整个包含在try 块中的函数,在这种情况下不是把try 块放在函数定义的内部,而是把函数体整个包含在一个函数try 块中,即try块的花括号包含整个函数体,这样把程序的正常处理代码和异常处理代码分离得很清楚。,16.2.3 捕获和处理异常,C+异常处理机制将出现异常后的处理程序放在catch块中。当一个异常被try 块中的语句抛出时,系统通过查看跟在try 块后面的catch 块列表,来查找能够处理该异常的catch 块。其语法格式为: catch() /异常处理代码 异常类型声明与函数的形参是类似,可以是某个类型的值,也可以是引用,它指出了子句处理的异常的类型。异常类型可以是任何有效的数据类型,单个类型或单个对象声明。在try块后,可以有一个或多个catch块,每个catch块称为一个异常处理块。一个catch 块由三部分构成:关键字catch、在括号中的异常类型说明和复合语句中的一组语句。如果选择了一个catch 块来处理一个异常,则执行相应的复合语句。一般情况下,try后面的catch块列表中,一个异常类型只能出现一次。,程序运行时,在try块内的通过在“throw表达式;”代码,抛出一个异常,并立即跳转到try后的catch块列表中,按照catch块出现的先后顺序,查找异常类型声明与被抛掷的异常类型相同的catch块。找到后,将抛出的异常对象的值赋给对应catch块的“异常类型声明”中的异常对象名,并执行该段异常处理程序。执行完该catch块代码后,跳过和其并列的其他的catch块到trycatch后面的语句执行。这种查找对应异常处理的过程称为异常事件类型匹配。,16.2.4 异常处理的执行过程,1. 异常处理的过程 整个异常处理的过程为: (1)先把可能出现异常的、需要检查的语句或程序段放在try 后面的花括号中。 (2)如果在执行保护段内没有发生异常,跳过try块后的所有catch块,转到trycatch子句后面的语句继续执行。
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!