《C++语言程序设计教程.pptx》由会员分享,可在线阅读,更多相关《C++语言程序设计教程.pptx(176页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、C+语言程序设计教程语言程序设计教程(第(第6-10章)章)第第6章章 类与对象类与对象6.1 从面向过程到面向对象从面向过程到面向对象6.2 类与对象的定义类与对象的定义6.3 对象的初始化对象的初始化6.4 对象数组与对象指针对象数组与对象指针6.5 静态成员静态成员6.6 友元友元6.7 常对象和常成员常对象和常成员6.8 程序实例程序实例6.1 从面向过程到面向对象从面向过程到面向对象6.1.1 面向对象程序设计的基本概念面向对象程序设计的基本概念1对象与方法对象与方法对象是指现实世界中具体存在的实体。每一个对象是指现实世界中具体存在的实体。每一个对象都有自己的属性(包括自己特有的属性
2、和对象都有自己的属性(包括自己特有的属性和同类对象的共同属性)。属性反映对象自身状同类对象的共同属性)。属性反映对象自身状态变化,表现为当前的属性值。态变化,表现为当前的属性值。方法是用来描述对象动态特征的一个操作序列。方法是用来描述对象动态特征的一个操作序列。消息是用来请求对象执行某一操作或回答某些消息是用来请求对象执行某一操作或回答某些信息的要求。实际上是一个对象对另一个对象信息的要求。实际上是一个对象对另一个对象的调用。的调用。2类类类是具有相同属性和方法的一组对象的集合,类是具有相同属性和方法的一组对象的集合,它为属于该类的全部对象提供了统一的抽象描它为属于该类的全部对象提供了统一的抽
3、象描述。将相似的对象分组形成一个类,每个这样述。将相似的对象分组形成一个类,每个这样的对象被称为类的一个实例,一个类中的所有的对象被称为类的一个实例,一个类中的所有对象共享一个公共的定义,尽管它们对属性所对象共享一个公共的定义,尽管它们对属性所赋予的值不同。赋予的值不同。3封装封装封装(封装(Encapsulation)是指把对象属性和操)是指把对象属性和操作结合在一起,构成独立的单元,它的内部作结合在一起,构成独立的单元,它的内部信息对外界是隐蔽的,不允许外界直接存取信息对外界是隐蔽的,不允许外界直接存取对象的属性,只能通过有限的接口与对象发对象的属性,只能通过有限的接口与对象发生联系。生联
4、系。4继承继承继承(继承(Inheritance)反映的是类与类之间抽象级)反映的是类与类之间抽象级别的不同,根据继承与被继承的关系,可分为基别的不同,根据继承与被继承的关系,可分为基类和衍类,基类也称为父类,衍类也称为子类。类和衍类,基类也称为父类,衍类也称为子类。子类将从父类那里获得所有的属性和方法,并且子类将从父类那里获得所有的属性和方法,并且可以对这些获得的属性和方法加以改造,使之具可以对这些获得的属性和方法加以改造,使之具有自己的特点。一个父类可以派生出若干子类,有自己的特点。一个父类可以派生出若干子类,每个子类都可以通过继承和改造获得自己的一套每个子类都可以通过继承和改造获得自己的
5、一套属性和方法,由此,父类表现出的是共性和一般属性和方法,由此,父类表现出的是共性和一般性,子类表现出的是个性和特性,父类的抽象级性,子类表现出的是个性和特性,父类的抽象级别高于子类。继承具有传递性。继承使得程序设别高于子类。继承具有传递性。继承使得程序设计人员可以在已有的类的基础上定义和实现新类,计人员可以在已有的类的基础上定义和实现新类,所以有效地支持了软件构件的复用。所以有效地支持了软件构件的复用。5多态性多态性不同的对象收到相同的消息产生不同的动作,不同的对象收到相同的消息产生不同的动作,这种功能称为多态性(这种功能称为多态性(Polymorphism)。将多)。将多态的概念应用于面向
6、对象程序设计,增强了程态的概念应用于面向对象程序设计,增强了程序对客观世界的模拟性,使得对象程序具有了序对客观世界的模拟性,使得对象程序具有了更好的可读性,更易于理解,而且显著提高了更好的可读性,更易于理解,而且显著提高了软件的可复用性和可扩充性。软件的可复用性和可扩充性。6.1.2 C+面向对象程序的结构面向对象程序的结构一个面向对象的一个面向对象的C+程序一般由类的声明和类程序一般由类的声明和类的使用两部分组成。类的使用部分一般由主函的使用两部分组成。类的使用部分一般由主函数和有关子函数组成。数和有关子函数组成。以下是一个典型的以下是一个典型的C+程序结构。程序结构。#include/类的
7、定义部分类的定义部分class C int x,y,z;/类类C的数据成员声明的数据成员声明f();/类类C的成员函数声明的成员函数声明;/类的使用部分类的使用部分void main()C a;/建立一个类建立一个类C的对象的对象aa.f();/给对象给对象a发消息,调用成员函数发消息,调用成员函数f()在在C+程序中,程序设计始终围绕程序中,程序设计始终围绕“类类”展开。展开。通过声明类,构建了程序所要完成的功能,体通过声明类,构建了程序所要完成的功能,体现了面向对象程序设计的思想。下面看一个具现了面向对象程序设计的思想。下面看一个具体的例子,直观地了解一下面向对象程序设计体的例子,直观地了
8、解一下面向对象程序设计方法与结构化程序设计方法的区别。方法与结构化程序设计方法的区别。【例例6.1】类的应用示例。类的应用示例。6.2 类与对象的定义类与对象的定义6.2.1 类的定义类的定义在在C+中,一个类指定一个独立的对象集合,中,一个类指定一个独立的对象集合,该对象集合由组成该类的对象以及这些对象所该对象集合由组成该类的对象以及这些对象所允许的操作组成。允许的操作组成。1类的定义形式类的定义形式类定义的一般形式如下:类定义的一般形式如下:class 类名类名 public:数据成员或成员函数的定义数据成员或成员函数的定义private:数据成员或成员函数的定义数据成员或成员函数的定义p
9、rotected:数据成员或成员函数的定义数据成员或成员函数的定义;2类成员函数的定义类成员函数的定义对类的成员函数的定义通常有两种形式,一种对类的成员函数的定义通常有两种形式,一种是在类的定义中直接定义函数,一种是在类外是在类的定义中直接定义函数,一种是在类外定义。前面的例定义。前面的例6.1就是在类内部实现成员函就是在类内部实现成员函数,下面再看一个例子。数,下面再看一个例子。【例例6.2】已知已知y,当,当f(n)122334n(n1)时,求时,求y的值。的值。按照类的定义形式,可以在类定义中只给出成按照类的定义形式,可以在类定义中只给出成员函数的原型,而在类外部定义具体的成员函员函数的
10、原型,而在类外部定义具体的成员函数。这种成员函数在类外定义的一般形式如下:数。这种成员函数在类外定义的一般形式如下:函数返回值的类型函数返回值的类型 类名类名:函数名函数名(形参表形参表)(函数体)(函数体)其中双冒号其中双冒号:是作用域运算符,它指出该函数是作用域运算符,它指出该函数是属于哪一个类的成员函数。是属于哪一个类的成员函数。6.2.2 对象的定义与使用对象的定义与使用1对象的定义对象的定义对象的定义形式如下:对象的定义形式如下:类名类名 对象名表对象名表;其中对象名表代表有多个对象名,各对象名之其中对象名表代表有多个对象名,各对象名之间以逗号分隔。间以逗号分隔。2对象成员引用对象成
11、员引用具体引用形式为:具体引用形式为:对象名对象名.数据成员名数据成员名对象名对象名.成员函数名成员函数名(实参表实参表)【例例6.3】定义一个时钟类,类中有定义一个时钟类,类中有3个私有个私有数据成员(数据成员(Hour、Minute和和Second)和两个)和两个公有成员函数(公有成员函数(SetTime和和ShowTime)。)。SetTime根据传递的根据传递的3个参数为对象设置时间,个参数为对象设置时间,ShowTime负责将对象表示的时间显示输出。负责将对象表示的时间显示输出。在主函数中,建立一个时间类的对象,先利用在主函数中,建立一个时间类的对象,先利用默认时间设置,再设置时间为
12、默认时间设置,再设置时间为10时时23分分45秒并秒并显示该时间。显示该时间。6.2.3 类与结构体的区别类与结构体的区别在在C+语言中,结构体除了具有原先语言中,结构体除了具有原先C语言定义语言定义的功能外,还具有类似于类的功能,即也可以的功能外,还具有类似于类的功能,即也可以在其中定义函数。它们之间的区别是:在结构在其中定义函数。它们之间的区别是:在结构体中,成员的默认访问权限是体中,成员的默认访问权限是public,而类成员,而类成员的默认访问权限是的默认访问权限是private。【例例6.4】用结构体定义类的示例。用结构体定义类的示例。6.3 对象的初始化对象的初始化在类的定义中不能给
13、数据成员赋初值。在类的定义中不能给数据成员赋初值。从封装的目的出发,类的数据成员应该多为私从封装的目的出发,类的数据成员应该多为私有的,对私有数据成员的访问只能通过成员函有的,对私有数据成员的访问只能通过成员函数,而不能通过成员引用的方式来赋值。数,而不能通过成员引用的方式来赋值。C+中定义了一种特殊的初始化函数,称之为中定义了一种特殊的初始化函数,称之为构造函数(构造函数(Constructor)。在特定对象使用结)。在特定对象使用结束时,还将进行一些清除工作。对象清除工作束时,还将进行一些清除工作。对象清除工作由析构函数(由析构函数(Destructor)来完成。)来完成。6.3.1 构造
14、函数构造函数1构造函数的特点构造函数的特点(1)构造函数名与类名相同,且没有返回值,)构造函数名与类名相同,且没有返回值,不能指定函数类型。不能指定函数类型。(2)构造函数必须使具有公有属性,但它不能)构造函数必须使具有公有属性,但它不能像其它成员函数那样被显式地调用,它是在定像其它成员函数那样被显式地调用,它是在定义对象的同时被系统自动调用的。义对象的同时被系统自动调用的。(3)构造函数是特殊的成员函数,函数体可以)构造函数是特殊的成员函数,函数体可以写在类体内,也可以写在类体外。写在类体内,也可以写在类体外。(4)构造函数可以重载,即一个类中可以定义)构造函数可以重载,即一个类中可以定义多
15、个参数个数或参数类型不同的构造函数。多个参数个数或参数类型不同的构造函数。【例例6.5】使用构造函数替代例使用构造函数替代例6.3中中SetTime()成员函数,并在主函数中,使用构造函数设置成员函数,并在主函数中,使用构造函数设置时间为时间为15时时19分分56秒并显示该时间。秒并显示该时间。构造函数也可以重载。关于重载的概念将在第构造函数也可以重载。关于重载的概念将在第7章详细介绍,这里先看一个例子。章详细介绍,这里先看一个例子。【例例6.6】构造函数重载定义示例。构造函数重载定义示例。综上所述,构造函数是一个有着特殊名字,在综上所述,构造函数是一个有着特殊名字,在对象创建时被自动调用的一
16、种函数,它的功能对象创建时被自动调用的一种函数,它的功能就是完成对象的初始化。就是完成对象的初始化。2默认的构造函数默认的构造函数如果类定义中没有给出构造函数,则如果类定义中没有给出构造函数,则C+编译器自动编译器自动给出一个默认的构造函数,而且默认的构造函数只能给出一个默认的构造函数,而且默认的构造函数只能有一个,形式如下:有一个,形式如下:类名类名:默认构造函数名默认构造函数名()若没有定义过任何形式的构造函数,系统会自动生成若没有定义过任何形式的构造函数,系统会自动生成默认的构造函数。若已经定义过构造函数,则系统不默认的构造函数。若已经定义过构造函数,则系统不会自动生成默认的构造函数,一
17、旦需要,则要求显式会自动生成默认的构造函数,一旦需要,则要求显式地定义这种形式的构造函数。在程序中,若定义一个地定义这种形式的构造函数。在程序中,若定义一个静态对象而没有指明初始值时,编译器会按默认的构静态对象而没有指明初始值时,编译器会按默认的构造函数对对象的数据成员初始化为造函数对对象的数据成员初始化为0或空。或空。【例例6.7】默认构造函数示例。默认构造函数示例。【例例6.8】构造函数的调用。构造函数的调用。6.3.2 析构函数析构函数1析构函数的特点析构函数的特点当对象创建时,会自动调用构造函数进行初始当对象创建时,会自动调用构造函数进行初始化。当对象撤消时,也会自动调用析构函数进化。
18、当对象撤消时,也会自动调用析构函数进行一些清理工作,如释放分配给对象的内存空行一些清理工作,如释放分配给对象的内存空间等。与构造函数类似的是:析构函数也与类间等。与构造函数类似的是:析构函数也与类同名,但在名字前有一个同名,但在名字前有一个“”符号,析构函符号,析构函数也具有公有属性,也没有返回类型和返回值,数也具有公有属性,也没有返回类型和返回值,但析构函数不带参数,不能重载,所以析构函但析构函数不带参数,不能重载,所以析构函数只有一个。数只有一个。【例例6.9】析构函数程序举例。析构函数程序举例。2默认的析构函数默认的析构函数和默认构造函数一样,如果类定义中没有给出析构函和默认构造函数一样
19、,如果类定义中没有给出析构函数,系统也会自动生成一个默认的析构函数,其格式数,系统也会自动生成一个默认的析构函数,其格式如下:如下:类名称类名称:默认析构函数名默认析构函数名()例如,编译系统为类例如,编译系统为类Point生成默认的析构函数如下:生成默认的析构函数如下:Point:Point()对于大多数类而言,默认的析构函数就能满足要求。对于大多数类而言,默认的析构函数就能满足要求。只有在一个对象完成其操作之前需要做一些内部处理只有在一个对象完成其操作之前需要做一些内部处理时,才显式地定义析构函数。时,才显式地定义析构函数。6.3.3 复制构造函数复制构造函数复制构造函数的作用是使用一个已
20、存在的对象复制构造函数的作用是使用一个已存在的对象去初始化另一个同类对象,它也是一种构造函去初始化另一个同类对象,它也是一种构造函数,除了具有一般构造函数的特征外,它还具数,除了具有一般构造函数的特征外,它还具有如下特点:有如下特点:(1)其形参必须是本类的对象的引用。)其形参必须是本类的对象的引用。(2)某函数的形参是类的对象,调用该函数需)某函数的形参是类的对象,调用该函数需要复制构造函数进行形参和实参结合。要复制构造函数进行形参和实参结合。(3)函数的返值是类的对象,函数调用返回的)函数的返值是类的对象,函数调用返回的时候需要调用复制构造函数实现类对象的赋值。时候需要调用复制构造函数实现
21、类对象的赋值。复制构造函数的定义格式如下:复制构造函数的定义格式如下:类名类名:复制构造函数名复制构造函数名(const 类名类名&对象名对象名)(函数体函数体)复制构造函数与类同名,复制构造函数与类同名,const是类型修饰符,是类型修饰符,被其修饰的对象是个不能被更新的常量。被其修饰的对象是个不能被更新的常量。【例例6.10】默认复制构造函数示例。默认复制构造函数示例。【例例6.11】复制构造函数示例。复制构造函数示例。普通构造函数在建立对象时被调用,而复制构造函数普通构造函数在建立对象时被调用,而复制构造函数在用已有对象初始化一个新对象时被调用。复制构造在用已有对象初始化一个新对象时被调
22、用。复制构造函数被调用通常发生在以下函数被调用通常发生在以下3种情况:种情况:(1)程序中需要新建一个对象,并用一个类的对象)程序中需要新建一个对象,并用一个类的对象去初始化类的另一个对象的时候。去初始化类的另一个对象的时候。(2)当对象作函数参数时,调用该函数时需要将实)当对象作函数参数时,调用该函数时需要将实参对象完整地传递给形参,这就需要按实参复制一个参对象完整地传递给形参,这就需要按实参复制一个形参,系统是通过调用复制构造函数来实现的,这样形参,系统是通过调用复制构造函数来实现的,这样能保证形参具有和实参完全相同的值。能保证形参具有和实参完全相同的值。(3)当函数的返回值是类的对象。在
23、函数调用完毕)当函数的返回值是类的对象。在函数调用完毕需要将返回值带回函数调用处时,此时需要将函数中需要将返回值带回函数调用处时,此时需要将函数中的对象复制一个临时对象并传给该函数的调用处。的对象复制一个临时对象并传给该函数的调用处。以上以上3种调用复制构造函数都是由编译系统自动完成种调用复制构造函数都是由编译系统自动完成的,不必由用户自己去调用。的,不必由用户自己去调用。6.4 对象数组与对象指针对象数组与对象指针6.4.1 对象数组对象数组对象数组是指数组的每一个元素都是相同类型对象的对象数组是指数组的每一个元素都是相同类型对象的数组,也就是说,若一个类有若干个对象,把这一系数组,也就是说
24、,若一个类有若干个对象,把这一系列的对象用一个数组来表示。对象数组的元素是对象,列的对象用一个数组来表示。对象数组的元素是对象,不仅具有数据成员,而且还有成员函数。不仅具有数据成员,而且还有成员函数。对象数组的定义和普通数组的定义类似,一般格式如对象数组的定义和普通数组的定义类似,一般格式如下:下:类名类名 数组名数组名第一维大小第一维大小第二维数组大小第二维数组大小其中,类名是指该数组元素属于该类的对象,方括号其中,类名是指该数组元素属于该类的对象,方括号内的数组大小给出了某一维元素的个数。一维对象数内的数组大小给出了某一维元素的个数。一维对象数组只有一对方括号,二维对象数组要有两个方括号对
25、,组只有一对方括号,二维对象数组要有两个方括号对,等等。等等。与普通数组一样,在使用对象数组时也只能访与普通数组一样,在使用对象数组时也只能访问单个数组元素,也就是一个对象,通过这个问单个数组元素,也就是一个对象,通过这个对象,可以访问它的公有成员,一般形式如下:对象,可以访问它的公有成员,一般形式如下:数组名数组名下标下标.成员名成员名和普通数组一样,对象数组既可以在定义时初和普通数组一样,对象数组既可以在定义时初始化,也可以在定义后赋值。始化,也可以在定义后赋值。【例例6.12】对象数组应用示例。对象数组应用示例。6.4.2 对象指针对象指针对象指针就是指向对象的指针,其定义的格式如下:对
26、象指针就是指向对象的指针,其定义的格式如下:类名类名*对象指针名对象指针名;对象成员也可以通过指向对象的指针来引用,引用数对象成员也可以通过指向对象的指针来引用,引用数据成员的具体形式如下:据成员的具体形式如下:指向对象的指针指向对象的指针-数据成员名数据成员名或:或:(*指向对象的指针指向对象的指针).数据成员名数据成员名引用成员函数的具体形式如下:引用成员函数的具体形式如下:指向对象的指针指向对象的指针-成员函数名成员函数名(实参表实参表)或:或:(*指向对象的指针指向对象的指针).成员函数名成员函数名(实参表实参表)【例例6.13】对象指针应用示例。对象指针应用示例。6.4.3 指向类成
27、员的指针指向类成员的指针1指向数据成员的指针指向数据成员的指针指向数据成员的指针定义格式如下:指向数据成员的指针定义格式如下:类型说明符类型说明符 类名类名:*数据成员指针名数据成员指针名;定义了指向数据成员的指针后,需要对其进行赋值,定义了指向数据成员的指针后,需要对其进行赋值,也就是要确定指向类的哪一个成员。对数据成员指针也就是要确定指向类的哪一个成员。对数据成员指针赋值的一般格式如下:赋值的一般格式如下:数据成员指针名数据成员指针名=&类名类名:数据成员名数据成员名;将指针指向类的数据成员后,就可以通过类的对象引将指针指向类的数据成员后,就可以通过类的对象引用指针所指向的数据成员,其格式
28、有两种:用指针所指向的数据成员,其格式有两种:对象名对象名.*数据成员指针名数据成员指针名;或:或:对象指针名对象指针名-*数据成员指针名数据成员指针名;【例例6.14】指向数据成员指针应用举例。指向数据成员指针应用举例。2指向成员函数的指针指向成员函数的指针指向成员函数的指针定义格式如下:指向成员函数的指针定义格式如下:函数返回值类型函数返回值类型(类名类名:*成员函数指针名成员函数指针名)(参数表参数表);定义成员函数指针后要对其赋值,也就是确定指向类定义成员函数指针后要对其赋值,也就是确定指向类的哪一个成员函数。给指向成员函数指针赋值的一般的哪一个成员函数。给指向成员函数指针赋值的一般格
29、式如下:格式如下:成员函数指针名成员函数指针名=&类名类名:成员函数名成员函数名;调用成员函数指针所指向函数的格式如下:调用成员函数指针所指向函数的格式如下:(对象名对象名.*成员函数指针名成员函数指针名)(实参表实参表)或:或:(对象指针名对象指针名-*成员函数指针名成员函数指针名)(实参表实参表)【例例6.15】指向类成员函数指针应用举例。指向类成员函数指针应用举例。6.4.4 this指针指针类的每一个成员函数都有一个隐含的常量指针,类的每一个成员函数都有一个隐含的常量指针,通常称为通常称为this指针。指针。this指针的类型就是成员函指针的类型就是成员函数所属类的类型。当调用成员函数
30、时,它被初数所属类的类型。当调用成员函数时,它被初始化为被调用函数的对象的地址。始化为被调用函数的对象的地址。this指针在系指针在系统中是隐含地存在的,也可以显式地使用。统中是隐含地存在的,也可以显式地使用。【例例6.16】this指针应用举例。指针应用举例。需要注意的是,需要注意的是,this指针是一个指针是一个const指针,不指针,不能在程序中修改它,而且能在程序中修改它,而且this指针的作用域仅在指针的作用域仅在一个对象的内部。一个对象的内部。6.5 静态成员静态成员6.5.1 静态数据成员静态数据成员静态数据成员的定义格式如下:静态数据成员的定义格式如下:static 数据类型数
31、据类型 变量名变量名;静态数据成员初始化的方式也与一般的数据成员不同。静态数据成员初始化的方式也与一般的数据成员不同。静态数据成员初始化应在类外进行,而且应在对象定静态数据成员初始化应在类外进行,而且应在对象定义之前。一般在义之前。一般在main()函数之前,类定义之后的位置函数之前,类定义之后的位置对它进行初始化,格式如下:对它进行初始化,格式如下:数据类型数据类型 类名类名:静态数据成员名初始值静态数据成员名初始值;引用静态数据成员的格式如下:引用静态数据成员的格式如下:类名类名:静态数据成员名静态数据成员名【例例6.17】静态数据成员应用举例。静态数据成员应用举例。6.5.2 静态成员函
32、数静态成员函数定义静态成员函数的格式如下:定义静态成员函数的格式如下:static 函数返回值的类型函数返回值的类型 静态成员函数名静态成员函数名(形参形参表表)(函数体函数体)静态成员函数仅能访问静态的数据成员,不能访问非静态成员函数仅能访问静态的数据成员,不能访问非静态的数据成员,也不能访问非静态的成员函数,这静态的数据成员,也不能访问非静态的成员函数,这是由于静态的成员函数没有是由于静态的成员函数没有this指针。静态成员函数的指针。静态成员函数的调用不需要对象名。类似于静态的数据成员,公有的、调用不需要对象名。类似于静态的数据成员,公有的、静态的成员函数在类外的调用方式如下:静态的成员
33、函数在类外的调用方式如下:类名类名:静态成员函数名静态成员函数名(实参表实参表)也允许用对象或指向对象的指针调用静态成员函数,也允许用对象或指向对象的指针调用静态成员函数,一般格式如下:一般格式如下:对象名对象名.静态成员函数名静态成员函数名(实参表实参表)对象指针对象指针-静态成员函数名静态成员函数名(实参表实参表)【例例6.18】静态成员函数应用举例。静态成员函数应用举例。6.6 友元友元6.6.1 友元函数友元函数友元函数是类定义中由关键字友元函数是类定义中由关键字friend修饰的非成修饰的非成员函数。友元函数可以是一个普通函数,也可员函数。友元函数可以是一个普通函数,也可以是其它类的
34、成员函数,它不是本类的成员函以是其它类的成员函数,它不是本类的成员函数,但是在它的函数体中可以通过数,但是在它的函数体中可以通过“对象对象.成员成员名名”访问类的私有成员和保护成员。访问类的私有成员和保护成员。友元函数声明的格式为:友元函数声明的格式为:friend 函数返回值类型函数返回值类型 友元函数名友元函数名(参数表参数表);【例例6.19】友元函数应用举例。友元函数应用举例。6.6.2 友元类友元类和函数一样,类也可以说明为另一个类的友元,和函数一样,类也可以说明为另一个类的友元,这时称该类为友元类。如果这时称该类为友元类。如果A类是类是B类的友元类,类的友元类,则则A类中的私有成员
35、函数都是类中的私有成员函数都是B类的友元函数,类的友元函数,都可以访问都可以访问B类的私有和保护成员。友元类说类的私有和保护成员。友元类说明的格式为:明的格式为:friend class 类名类名;类说明语句可以放在公有部分,也可以放在私类说明语句可以放在公有部分,也可以放在私有部分或保护部分。有部分或保护部分。【例例6.20】友元类应用举例。友元类应用举例。友元提供了一种非成员函数访问类的私有友元提供了一种非成员函数访问类的私有成员的方法,这在某些情况下为程序设计成员的方法,这在某些情况下为程序设计提供了一定的方便性。但是面向对象的程提供了一定的方便性。但是面向对象的程序设计要求类的接口与类
36、的实现分开,对序设计要求类的接口与类的实现分开,对对象的访问通过其接口函数进行。如果直对象的访问通过其接口函数进行。如果直接访问对象的私有成员,就破坏了面向对接访问对象的私有成员,就破坏了面向对象程序的数据隐藏和封装特性,虽然提供象程序的数据隐藏和封装特性,虽然提供了一些方便,但有可能是得不偿失的,所了一些方便,但有可能是得不偿失的,所以,需要慎用友元。以,需要慎用友元。此外,还有两点需要注意:此外,还有两点需要注意:(1)友元关系不能传递。例如,)友元关系不能传递。例如,B类是类是A类的类的友元,友元,C类是类是B类的友元,如果类的友元,如果C类和类和A类之间类之间没有显式说明,没有显式说明
37、,C类和类和A类之间不是友元关系。类之间不是友元关系。(2)友元关系的单向性。例如,如果)友元关系的单向性。例如,如果B类是类是A类的友元,则类的友元,则B类的成员函数都是类的成员函数都是A类的友元函类的友元函数,可以访问数,可以访问A类的所有数据成员,但类的所有数据成员,但A类的成类的成员函数就不是员函数就不是B类的友元函数,也就不能访问类的友元函数,也就不能访问B类的所有数据成员。类的所有数据成员。6.7 常对象和常成员常对象和常成员6.7.1 常对象和常成员函数常对象和常成员函数如果在定义对象时用如果在定义对象时用const修饰,则被定义的对修饰,则被定义的对象为常对象。常对象的数据成员
38、值在对象的整象为常对象。常对象的数据成员值在对象的整个生存期内不能被改变,常对象的定义形式如个生存期内不能被改变,常对象的定义形式如下:下:类名类名 const 对象名对象名(参数表参数表);或:或:const 类名类名 对象名对象名(参数表参数表);在定义常对象时必须进行初始化,而且不能被在定义常对象时必须进行初始化,而且不能被更新。更新。常成员函数的声明格式如下:常成员函数的声明格式如下:函数返回值的类型函数返回值的类型 函数名函数名(参数表参数表)const;const是函数类型的组成部分,因此在函数的实是函数类型的组成部分,因此在函数的实现部分也要带现部分也要带const关键字。常成员
39、函数表示该关键字。常成员函数表示该成员函数只能读类数据成员,而不能修改类的成员函数只能读类数据成员,而不能修改类的数据成员。定义常成员函数时,把数据成员。定义常成员函数时,把const关键字关键字放在函数的参数表和函数体之间。放在函数的参数表和函数体之间。【例例6.21】常对象和常成员函数应用举例。常对象和常成员函数应用举例。6.7.2 常数据成员常数据成员带有成员初始化列表的构造函数的一般形式如下:带有成员初始化列表的构造函数的一般形式如下:类名类名:构造函数名构造函数名(参数表参数表):(成员初始化列表成员初始化列表)/构造函数体构造函数体成员初始化列表的一般形式如下:成员初始化列表的一般
40、形式如下:数据成员名数据成员名1(初始值初始值1),数据成员名数据成员名2(初始值初始值2),【例例6.22】常数据成员应用举例。常数据成员应用举例。6.8 程序实例程序实例【例例6.23】已知,求:已知,求:(1)yy)?x:y);double max(double x,double y)return(xy)?x:y);#include void main()coutmax(10,20)endl;coutmax(1.23,2.34)endl;重载方式重载方式靠将实参的个数及类型和所被调用的f()函数的形参的个数及类型一一比较来判断。()作为重载函数至少在参数个数、参数类型上有所不同。若仅在返
41、回类型上不同,编译器是无法区别的。如:void func(int);int func(int);void func(int);int func(int);/错误 int min(int,int);int min(int,int);int min(int,int,int);int min(int,int,int);/正确 int add(int,int);int add(int,int);double add(double,double);double add(double,double);/正确()typedef定义的类型只是给已有类型取另外一个名字,编译器不能将其同原类型区分。如:typed
42、ef INT int;typedef INT int;void func(int x).void func(int x).void func(INT x)void func(INT x)(3)重载函数一般应具有相同的功能,否则会破坏程序的可读性。注意事项注意事项问题的提出问题的提出1、基本数据类型系统提供了大量运算符。、基本数据类型系统提供了大量运算符。如:如:intx,y;x=x+y;表达简洁,使用方便。表达简洁,使用方便。2、问题的提出:、问题的提出:对于串类的对象合并:对于串类的对象合并:stringx,y;strcat(x,y);不如上述运算那样简单,希望能改造不如上述运算那样简单,希
43、望能改造为:为:x=x+y;更加简单和直观更加简单和直观!因此,需要对因此,需要对“+”进行不同的解释,即:重载进行不同的解释,即:重载7.1.2运算符重载运算符重载一、一、C+中的运算符中的运算符1、大多数系统预定义运算符都能重载,除开以下:、大多数系统预定义运算符都能重载,除开以下:.:?:#*(当乘法运算符时可重载当乘法运算符时可重载)“.”“:”“*”在在C+中有特定定义中有特定定义 “?:”不值得重载不值得重载 “#”不能被重载不能被重载2、重载时的注意事项:、重载时的注意事项:(1)不能改变优先级;不能改变优先级;(2)不能改变结合性;不能改变结合性;(3)不能改变运算符所需操作数
44、的个数;不能改变运算符所需操作数的个数;(4)重载后,可按这些运算符的表达方式使用。重载后,可按这些运算符的表达方式使用。一、通过运算符重载函数进行重载一、通过运算符重载函数进行重载1、运算符重载函数是成员函数、运算符重载函数是成员函数 语法形式:语法形式:TypeX:operator(参数表参数表)/相对于该类而定义的操作相对于该类而定义的操作 运算符重载的语法形式运算符重载的语法形式返回类型返回类型重载该运算符的重载该运算符的类名类名要重载的运算符要重载的运算符符号符号2、运算符重载函数是友元函数、运算符重载函数是友元函数 语法形式:语法形式:Typeoperator(参数表参数表)/相对
45、于该类而定义的操作相对于该类而定义的操作 罗列运算符所罗列运算符所需要的操作数需要的操作数3、实例、实例voidoperator+()voidoperator-()voidcounter:operator+()if(value0)value-;注意:重载中注意的问题注意:重载中注意的问题1、运算符重载函数是友元函数、成员函数的区别;、运算符重载函数是友元函数、成员函数的区别;2、运算符被重载时,只是相对一特定类被重载,在特定的环、运算符被重载时,只是相对一特定类被重载,在特定的环境下作出特定的解释。当离开这个特定环境后,具有原来的境下作出特定的解释。当离开这个特定环境后,具有原来的意义意义(系
46、统定义系统定义);3、当重载运算符解释失败时,用该运算符的预定义版本、当重载运算符解释失败时,用该运算符的预定义版本(系统系统)进行解释;进行解释;4、用此方法重载时,只能对一个系统已有运算符进行特定环、用此方法重载时,只能对一个系统已有运算符进行特定环境下的新的解释,而不能创造新的运算符。当运算符不够用境下的新的解释,而不能创造新的运算符。当运算符不够用时,用函数调用来实现;时,用函数调用来实现;5、除、除“=”以外,重载的运算符可以被任何派生类所继承,以外,重载的运算符可以被任何派生类所继承,“=”需要每个类明确定义自己的解释;需要每个类明确定义自己的解释;6、重载可能让程序的可读性下降,
47、在使用时应模仿运算符的重载可能让程序的可读性下降,在使用时应模仿运算符的习惯用法习惯用法 。一、运算符重载函数定义的两种方式一、运算符重载函数定义的两种方式1、成员函数和友元函数、成员函数和友元函数2、差别的关键原因:、差别的关键原因:this指针指针二、一元运算符二、一元运算符1、不论前缀还是后缀,需要一个操作数、不论前缀还是后缀,需要一个操作数2、对于任意一元运算符、对于任意一元运算符:(1)(1)成员函数重载运算符成员函数重载运算符 定义定义 type x:operator type x:operator()()显式调用显式调用 objX.operator objX.operator()
48、()隐式调用隐式调用 objX objX 或:或:ojbXojbX(2)(2)友元函数重载运算符友元函数重载运算符 定义定义 type operator type operator(Xobj)(Xobj)显式调用显式调用 operatoroperator(obj X)(obj X)隐式调用隐式调用 ojbX ojbX 或:或:objX objX 一元和二元运算符一元和二元运算符方式 定义显式调用隐式调用备注成员函数Type X:operator()()objX.operator()()objX或:objX 操作数通过THIS指针隐含传递,因此参数表为空。友元函数Type operator(X
49、ojb)X ojb)operator(objX)(objX)objX或:objX()()所需要的一个操作数在参数表中,由对象显式地提供。一元运算符的重载一元运算符的重载三、二元运算符三、二元运算符1、需要两个操作数;、需要两个操作数;2、对于任意二元运算符、对于任意二元运算符:方式 定义显式调用隐式调用备注成员函数Type X:operator(X obj)(X obj)objX1.operator(objX2)(objX2)objX1(objX2)第一个操作数通过THIS指针隐含传递,第二个操作数通过参数提供,只有一个参数。友元函数Type operator(X ojb)(X ojb)ope
50、rator(objX1,(objX1,objX2)objX2)objX1(objX2)所需要的两个操作数在参数表中,由对象显式地提供。用成员函数重载运算符用成员函数重载运算符例例7-61、重载、重载“+”、“=”、“+”;读懂几个成员函数;读懂几个成员函数;2、成员函数重载运算符,激活运算符的对象都是由、成员函数重载运算符,激活运算符的对象都是由THIS指针隐含传递的。指针隐含传递的。用友元函数重载运算符用友元函数重载运算符1、成员函数重载可能造成的麻烦:成员函数重载的运算符不具有交换性。、成员函数重载可能造成的麻烦:成员函数重载的运算符不具有交换性。原因:成员函数仅能被一个原因:成员函数仅能