《(中职)第7章 类与对象okppt课件.ppt》由会员分享,可在线阅读,更多相关《(中职)第7章 类与对象okppt课件.ppt(44页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、在此输入您的封面副标题(中职)第7章 类与对象ok第第7章章 类与对象类与对象 7.1 类类7.2 对象对象 7.3 构造函数与析构函数构造函数与析构函数7.4 类与对象的进一步讨论类与对象的进一步讨论7.5 类的友元类的友元7.6 类模板类模板7.7 继承与派生继承与派生7.8 虚函数虚函数7.9 运算符重载运算符重载 7.1 类类 类是对一组客观对象的抽象,它将该组对象所具有的共同特征类是对一组客观对象的抽象,它将该组对象所具有的共同特征(包括结构特征和行为特征)进行归纳抽象,以说明该组对象的(包括结构特征和行为特征)进行归纳抽象,以说明该组对象的性质和能力。因此,一个类至少包含以下两个方
2、面的描述:类所性质和能力。因此,一个类至少包含以下两个方面的描述:类所有实例共同具有的属性或结构特征,称为类的数据成员;类所有有实例共同具有的属性或结构特征,称为类的数据成员;类所有实例共同具有的操作、行为或方法,称为类的成员函数。实例共同具有的操作、行为或方法,称为类的成员函数。 在在C+中,类是一种数据类型,与结构体有很多相似之处,中,类是一种数据类型,与结构体有很多相似之处,类可以看作是在传统意义上的结构体上又加上了成员函数的一种类可以看作是在传统意义上的结构体上又加上了成员函数的一种数据类型。同结构体一样,在使用类之前必须要定义类。类的定数据类型。同结构体一样,在使用类之前必须要定义类
3、。类的定义可看作由说明部分和实现部分两部分来组成的。义可看作由说明部分和实现部分两部分来组成的。 7.1.1 类的定义类的定义 说明部分用来说明该类中的成员,包括数据成员和成员函数的说明。成员函数是用来对数据成员进行操作的,又称为方法,实现部分是用来对成员函数的定义。 1类定义的说明部分类定义的说明部分 class 类名称类名称 public: 公有数据成员和成员函数公有数据成员和成员函数 protected: 保护数据成员和成员函数保护数据成员和成员函数 private: 私有数据成员和成员函数私有数据成员和成员函数 ; 公有类型(public)公有成员用关键字public声明,它们是类的外
4、部接口,任何外部访问都必须通过这个接口来进行。 私有类型(private)私有成员用关键字private声明。如果私有成员紧接着类名称声明,私有关键字private可以省略。私有成员完全隐藏在类中,实现了访问权限的有效控制。 保护类型(protected)保护成员用protected声明,它与私有成员基本相似,但它对类的派生类有影响。它除了可以被本类中的成员函数访问外,还可以被本类的派生类的成员函数访问。 其中class是定义类的关键字;类名称是标识符,用于惟一标识一个类,一般以“C”开头,以区别于其他标识符;一对大括号内是对类的说明,说明该类的所有成员。 类的成员分为数据成员和成员函数,分别
5、描述类所表达的问题属性和行为,是问题特性不可分割的两个方面。 2类定义的实现部分类定义的实现部分 类定义的实现部分就是对类中成员函数的定义。类的成员函数描述类的行为,又叫“方法”,是程序算法的实现部分,是对封装的数据进行操作的主要途径。在类定义的说明部分给出了成员函数的原型声明,说明函数返回值类型、函数名和函数的参数表,在类声明之外给出函数的具体实现,这是多数成员函数的定义方法。 与普通函数不同的是,类外定义成员函数时要指明其所属类的名称,具体形式为: 返回值类型返回值类型 类名类名:函数成员名函数成员名(参数表参数表) 函数体函数体 注意,在类声明外定义成员函数的实现时,必须在成员函数名前加
6、上所属类名和作用域操作符“:”,从而说明该函数所属的类。 7.1.2 类的成员函数类的成员函数 将成员函数声明为内联函数有两种方式:隐式声明和显式声明。 如果将成员函数的函数体直接放在类定义体内,则该成员函数即为内联函数,这种方法称为隐式声明。为了提高类声明的可读性,一般将函数定义写在类声明体外,而采用关键字inline显式声明其为内联函数。显示声明写在类声明体外的内联成员函数有两种方式,一种是在类声明体内的该函数的原型声明前加关键字inline,或者在类声明体外定义函数时,在函数返回值类型前面加上inline, 在类内定义的函数被看作内联函数(inline function),而在类外部定义
7、的函数默认情况下都是非内联函数。内联成员函数和普通内联函数相同,在编译时编译系统会将函数体插入到每个调用它的地方,这样可以减少函数调用时的时间开销,提高执行效率;缺点是增加了程序的代码长度;所以,一般把相对简单的成员函数声明为内联函数,而把较复杂的函数定义为非内联函数。 1内联成员函数内联成员函数 inline void CDate:ShowDate() coutYear/Month/Dayendl; 用关键字用关键字inline将成员函数将成员函数ShowDate()显式声明为内联函数显式声明为内联函数,与隐式声明的效果完全相同。 class CDate public: void SetDa
8、te(int y,int m,int d);/非内联成员函数 void ShowDate()/内联成员函数 coutYear/Month/Dayendl; private: int Year,Month,Day; ; 隐式声明隐式声明 类的成员函数也可以带有缺省参数值,其调用规则同普通函数相同。 声明成员函数带有的缺省参数值有两种方式,一种方式在声明成员函数带有的缺省参数值有两种方式,一种方式在类中声明该成员函数时给出缺省参数值,如:类中声明该成员函数时给出缺省参数值,如: void SetDate(int y=2008,int m=1,int d=1);2带缺省参数的成员函数带缺省参数的成员
9、函数另一种方式是在函数定义中给出缺省的参数值,如:另一种方式是在函数定义中给出缺省的参数值,如:void CDate:SetDate(int y=2008,int m=1,int d=1) Year=y; Month=m; Day=d;void main() Date Date1,Date2; Date1.SetDate(); Date2.SetDate(2008,8,24); Date1.ShowDate(); Date2.ShowDate();调用这个函数时不给出实参,就可以把日期设置为调用这个函数时不给出实参,就可以把日期设置为2008/1/1 函数重载是指同一个函数名对应着多个函数实现
10、。在C+中,函数重载允许同一作用域内的同一个函数名可以声明多个函数,它们可以完成不同的功能,可以带不同个数、不同类型的参数。 两个函数成为重载函数,必两个函数成为重载函数,必须具有如下特征:函数名相同,须具有如下特征:函数名相同,函数的作用域相同,函数的参数函数的作用域相同,函数的参数个数不同或参数类型不完全相同。个数不同或参数类型不完全相同。 不仅可以在类的成员函数上不仅可以在类的成员函数上实现重载,在构造函数上也可以实现重载,在构造函数上也可以实现函数的重载,而且构造函数实现函数的重载,而且构造函数的重载使得我们可以用多种方式的重载使得我们可以用多种方式来创建对象。来创建对象。3成员函数重
11、载成员函数重载 class My_class public: int abs(int x); double abs(double x); ; int My_class:abs(int x) return x0?x:x; double My_class:abs(double x) return xShowDate();tDate.ShowDate();在定义了在定义了CDate类的对象后,就类的对象后,就可以访问其公有成员了可以访问其公有成员了 注意:注意:在类的外部只能访问类的公有成员,不能访问类的私有成员和保护成员;在类的内部,所有成员之间都可以通过成员名直接访问,这就实现了访问的有效控制。
12、7.3 构造函数与析构函数构造函数与析构函数 构造函数的作用就是在对象创建时利用特定的值构造对象,构造函数的作用就是在对象创建时利用特定的值构造对象,将对象初始化为一个特定的状态,使此对象具有区别于彼对象的将对象初始化为一个特定的状态,使此对象具有区别于彼对象的特征。构造函数完成的是从一般到具体的过程,它在对象被创建特征。构造函数完成的是从一般到具体的过程,它在对象被创建的时候由系统自动调用。的时候由系统自动调用。 构造函数也是类的一个成员函数,除了具有一般成员函数构造函数也是类的一个成员函数,除了具有一般成员函数的特征之外,还有一些特殊的性质。构造函数的函数名与类名相的特征之外,还有一些特殊
13、的性质。构造函数的函数名与类名相同,而且它没有返回值,不能有任何返回类型,也不能使用同,而且它没有返回值,不能有任何返回类型,也不能使用void,但在构造函数体内可有无值的但在构造函数体内可有无值的return语句。构造函数通常被定义语句。构造函数通常被定义为公有函数,但是,除了在创建对象时由系统自动调用之外,其为公有函数,但是,除了在创建对象时由系统自动调用之外,其它任何过程都无法再调用到它,也就是只能一次性地影响对象数它任何过程都无法再调用到它,也就是只能一次性地影响对象数据成员的初值。据成员的初值。7.3.1 构造函数构造函数 定义构造函数使定义构造函数使CDate类的对象在生成时就获得
14、一个缺省的日期值,类类的对象在生成时就获得一个缺省的日期值,类CDate的定的定义修改如下:义修改如下: class CDate public: CDate(); /声明构造函数 CDate(int y,int m,int d); /声明重载构造函数 void SetDate(int y,int m,int d); void ShowDate(); private: int Year,Month,Day; ;构造函数的实现如下:构造函数的实现如下: CDate:CDate() /构造函数的定义 Year = 2008; Month = 1; Day = 1; CDate:CDate(int y
15、, int m, int d) /重载的构造函数的定义 Year =y; Month = m; Day = d; 7.3.2 析构函数析构函数 类的另一个特殊的成员函数是析构函数,它在对象生存期类的另一个特殊的成员函数是析构函数,它在对象生存期即将结束的时刻由系统自动调用。它的作用与构造函数相反,即将结束的时刻由系统自动调用。它的作用与构造函数相反,它执行一些在对象撤消前必须执行的清理任务,例如释放由构它执行一些在对象撤消前必须执行的清理任务,例如释放由构造函数申请分配的内存等。造函数申请分配的内存等。 析构函数的函数名也是固定的,就是在类名前边加一个析构函数的函数名也是固定的,就是在类名前边
16、加一个“”字符做前缀就构成了析构函数的函数名。与构造函数相同,字符做前缀就构成了析构函数的函数名。与构造函数相同,析构函数也是类的一个公有成员函数,被系统自动调用,而且析构函数也是类的一个公有成员函数,被系统自动调用,而且在定义析构函数时,同样不能指定任何的返回类型,也不能使在定义析构函数时,同样不能指定任何的返回类型,也不能使用用void,如果类中没声明析构函数,系统也会自动生成一个不,如果类中没声明析构函数,系统也会自动生成一个不做任何事情的缺省析构函数;与构造函数不同的是,构造函数做任何事情的缺省析构函数;与构造函数不同的是,构造函数可以重载,也可以带有缺省的参数值,而析构函数只能有一个
17、,可以重载,也可以带有缺省的参数值,而析构函数只能有一个,且不能接受任何参数。且不能接受任何参数。#include class CArray public: CArray(int size);/构造函数声明构造函数声明 CArray();/析构函数声明析构函数声明 int GetAt(int nIndex); void SetAt(int nIndex, int newElement);protected: int* m_Data;/存数组首地址存数组首地址 int m_Size;/数组中包含元素个数数组中包含元素个数;CArray:CArray(int size) /构造函数定义构造函数定义
18、 coutConstruct the objectn; m_Data = new intsize;/申请内存空间申请内存空间 m_Size = size;/设置数组元素个数设置数组元素个数CArray:CArray()/析构函数定义析构函数定义 coutDestruct the objectn; delete m_Data;/释放内存空间释放内存空间int CArray:GetAt(int nIndex)/获取数组元素的值获取数组元素的值 return m_DatanIndex;void CArray:SetAt(int nIndex,int newElement)/设置数组元素的值设置数组元
19、素的值 m_DatanIndex = newElement;void main() CArray a(10); for (int i=0;i10;i+) a.SetAt(i,i); for (i=0;i10;i+) coutai=a.GetAt(i)”形式进行访问:形式进行访问: Dates-ShowDate(); (Dates+1)-ShowDate(); 若只有用户自定义的带有参数且无缺省值的构造函数,则若只有用户自定义的带有参数且无缺省值的构造函数,则必须在创建对象数组时使用构造函数进行初始化,其形式为:必须在创建对象数组时使用构造函数进行初始化,其形式为: 类名类名 数组名数组名常量表
20、达式常量表达式=类名类名(参数表参数表),类名类名(参数表参数表),类名类名(参数表参数表);2若类中只有需要参数的构造函数时若类中只有需要参数的构造函数时3若类的构造函数只需要若类的构造函数只需要1个参数时个参数时7.5 类的友元类的友元 友元提供了不同类的成员函数之间、类的成员函数与一般友元提供了不同类的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。也就是说,通过友元,一个普通函数之间进行数据共享的机制。也就是说,通过友元,一个普通函数或者其它类的成员函数可以访问到封装于某一个类中的数据函数或者其它类的成员函数可以访问到封装于某一个类中的数据 7.5.1 友元函数的定义及作用
21、友元函数的定义及作用 友元函数是在类定义中用关键字友元函数是在类定义中用关键字friend声明的非成员函数。声明的非成员函数。注意,友元函数可以是一个普通的函数,也可以是其它类的成员注意,友元函数可以是一个普通的函数,也可以是其它类的成员函数,它不是本类的成员函数,但是在它的函数体中可以通过对函数,它不是本类的成员函数,但是在它的函数体中可以通过对象使用本类的私有和保护成员。象使用本类的私有和保护成员。 在在CCircle类定义体中声明友元函数时,只给出了友元函数原型,类定义体中声明友元函数时,只给出了友元函数原型,友元函数友元函数fErr的实现定义在类外。可以看到在友元函数中通过对象名的实现
22、定义在类外。可以看到在友元函数中通过对象名直接访问了直接访问了CCircle类中的私有数据成员类中的私有数据成员R、X和和Y,这就是友元的关键,这就是友元的关键所在。所在。7.5.1 友元函数的定义及作用友元函数的定义及作用class CCircle public: /外部接口 CCircle(int rr=10,int xx=0,int yy=0) R=rr;X=xx;Y=yy; int GetR() return R; int GetX() return X; int GetY() return Y; friend double fErr(CCircle &a ,CCircle &b);
23、/声明友元函数private: int R,X,Y;double fErr(CCircle &a, CCircle &b) double x=double(c1.X-c2.X); double y=double(c1.Y-c2.Y); return (c1.R+c2.R)-(sqrt(x*x+y*y); 通过友元类声明,友元类的成员函数可以通过对象名直接访问到通过友元类声明,友元类的成员函数可以通过对象名直接访问到隐藏的数据,达到高效协调工作的目的。在较为复杂的问题中,实现隐藏的数据,达到高效协调工作的目的。在较为复杂的问题中,实现不同类之间的数据共享,友元类的使用也是很必要的选择。不同类之间
24、的数据共享,友元类的使用也是很必要的选择。7.5.2 友元类友元类 类也可以声明为另一个类的友元,称为友元类。一般的语法格式为:类也可以声明为另一个类的友元,称为友元类。一般的语法格式为: class B friend class A; ; 关于友元,还有两点需要注意:一是友元关系是不能传递的,关于友元,还有两点需要注意:一是友元关系是不能传递的,A类是类是B类的友元,类的友元,C类是类是A类的友元,类的友元,C类和类和B类之间,如果没有声明,类之间,如果没有声明,就没有任何友元关系,不能进行数据共享。第二,友元关系是单向的,就没有任何友元关系,不能进行数据共享。第二,友元关系是单向的,如果声
25、明如果声明A类是类是B类的友元,类的友元,A类的成员函数就可以访问类的成员函数就可以访问B类的私有和类的私有和保护数据,但保护数据,但B类的成员函数不能访问类的成员函数不能访问A类的私有、保护数据。类的私有、保护数据。7.6 类模板类模板 类是对象的抽象与描述,而类模板是类的抽象。类是对象的抽象与描述,而类模板是类的抽象。 7.6.1 类模板的定义类模板的定义类模板的定义形式:template class 类名类名;若在类声明体外定义类模板的成员函数,定义形式:template 函数类型函数类型 类名类名 :函数名函数名(参数列表参数列表)7.6.2 定义类模板对象定义类模板对象 声明了类模板
26、后,就可以用类模板定义对象了。当使用声明了类模板后,就可以用类模板定义对象了。当使用无参的构造函数创建对象时,定义形式为:无参的构造函数创建对象时,定义形式为: 类名类名 对象对象;若使用有参的构造函数创建对象时,定义形式为:若使用有参的构造函数创建对象时,定义形式为: 类名类名 对象对象(构造函数实参列表构造函数实参列表);7.7 继承与派生继承与派生 继承是继承是C+的另一个很重要的机制,这一机制支的另一个很重要的机制,这一机制支持面向对象设计思想中的层次概念。它允许一个类继承持面向对象设计思想中的层次概念。它允许一个类继承其它类的属性和功能,被继承的类称为基类或父类,继其它类的属性和功能
27、,被继承的类称为基类或父类,继承的类称为派生类或子类。派生类不仅可以继承基类的承的类称为派生类或子类。派生类不仅可以继承基类的功能和属性,还可以根据需要定义新的属性和功能,以功能和属性,还可以根据需要定义新的属性和功能,以剔除那些不适合其用途的操作,增加新的功能。因此,剔除那些不适合其用途的操作,增加新的功能。因此,继承可使用户重用基类的代码,专注于派生类的新代码,继承可使用户重用基类的代码,专注于派生类的新代码,提高代码的可重用性。提高代码的可重用性。 当派生类只有一个基类时,我们称这种继承方式当派生类只有一个基类时,我们称这种继承方式为单一继承,当派生类有多个基类时,则这种继承称多为单一继
28、承,当派生类有多个基类时,则这种继承称多重继承。重继承。7.7.1 派生类派生类定义单继承的派生类的形式:定义单继承的派生类的形式: class 派生类名:派生类名:继承方式继承方式 基类名基类名 派生类新增加的成员派生类新增加的成员 ;1定义派生类定义派生类 派生类名是要定义的新类的名字,是“基类名”的派生类,基类是已定义的类。派生类的继承方式有public、protected和private三种,也称为派生方式。这三种继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限,缺省情况下为私有派生。派生类中除了继承的基类的所有成员(不继承构造函数和析构函数)外,还可以增加自己的新
29、成员,则在类定义体内定义派生类新增加的数据成员和成员函数。派生类又称为导出类或子类,它具有如下特点: 可在基类所提供的基础上定义新成员。 可在自己类中隐藏基类的任何成员。 可在派生类中重新定义基类中的函数,即覆盖基类的成员函数可见,继承和派生的目的在于重用基类的某些属性,并对基类进行裁剪,以制定出适合于某种需要的新的类。2继承基类成员继承基类成员 由基类派生出派生类的目的是为了重用基类的特性,除了基类的构造函数和析构函数,派生类继承了基类的其余所有成员。类的派生方式有public(公有派生)、protected(保护派生)和private(私有派生)三种,不同的派生方式使得派生类对基类中具有不
30、同访问属性的成员的访问属性也有所不同, 对基类中的公有成员的继承若派生类是基类的公有派生,则基类中的公有成员在派生类中仍为公有成员;若派生类是基类的保护派生,则基类中的公有成员成为派生类的保护成员;若派生类是基类的私有派生,则基类中的公有成员在派生类中成为私有成员。 对基类中的保护成员的继承若派生类是基类的公有派生或保护派生,基类中的保护成员在派生类中仍是保护成员,仅能在派生类的成员中被使用,而不允许派生类的使用者访问,但可以被派生类的派生类继承。若派生类是基类的私有派生,基类中的保护成员都作为派生类的私有成员,只能被派生类的成员函数访问。 对基类的私有成员的继承无论派生类是基类的何种派生,派
31、生类都不能访问所继承的基类的私有成员。 7.7.2 派生类对基类成员的覆盖派生类对基类成员的覆盖 在派生类中可以定义同基类中的成员同名的成员,则在派生类中从基类继承来的该成员被编译器隐藏起来,从而可以实现派生类对从基类继承来的成员的覆盖。如果此时还要在派生类中访问基类的被隐藏的成员,则必须使用作用域运算符“:”以明确指定其类。 注意,作用域运算符“:”不能改变基类成员的访问性质,即基类成员是否能在派生类中被访问,取决于基类成员的访问性质和继承方式。 7.7.3 派生类的构造函数和析构函数派生类的构造函数和析构函数 要实现对继承来的基类成员的初始化,派生类的构造函数必须调用基类的构造函数。所以,
32、在定义派生类的构造函数时除了对自己的新增数据成员初始化外,还必须负责调用基类构造函数,使基类的数据成员得以初始化(若派生类中还有基类子对象,则还应该包含对子对象的初始化)。 假设派生类Y是从n个基类X1,X2,X3,Xn中派生的,则对于这种多继承的派生类的构造函数的定义形式为: Y:Y(参数表参数表0):X1(参数表参数表1),X2(参数表参数表2),Xn(参数表参数表n)而单一继承基类X的派生类Y的构造函数的定义形式为: Y:Y(参数表参数表):X(参数表参数表) 派生类中新数据成员初始化派生类中新数据成员初始化 1定义派生类的构造函数定义派生类的构造函数 类的构造函数与析构函数的特点均适合
33、于派生类的构造函数与析类的构造函数与析构函数的特点均适合于派生类的构造函数与析构函数。在构造派生类对象时,先要调用基类的构造函数,然后再调构函数。在构造派生类对象时,先要调用基类的构造函数,然后再调用派生类的构造函数;而析构函数的调用顺序刚好相反,先调用派生用派生类的构造函数;而析构函数的调用顺序刚好相反,先调用派生类的析构函数,再调用基类的析构函数。类的析构函数,再调用基类的析构函数。 创建派生类对象时,先调用基类的构造函数,对基类的数据成员进创建派生类对象时,先调用基类的构造函数,对基类的数据成员进行初始化,然后再调用派生类的构造函数。行初始化,然后再调用派生类的构造函数。 派生类是否定义
34、析构函数与基类无关。派生类是否定义析构函数与基类无关。 派生类的构造函数可以重载,以调用基类的不同的构造函数。派生类的构造函数可以重载,以调用基类的不同的构造函数。 派生类的构造函数体内一般只初始化派生类新增的数据成员,而不派生类的构造函数体内一般只初始化派生类新增的数据成员,而不直接初始化基类成员,即使能够直接访问它们。直接初始化基类成员,即使能够直接访问它们。 如果基类中根本没有定义构造函数或者有无参的构造函数,则派生如果基类中根本没有定义构造函数或者有无参的构造函数,则派生类构造函数的定义中可以省略对基类构造函数的调用。如果基类只有类构造函数的定义中可以省略对基类构造函数的调用。如果基类
35、只有使用一个或多个参数的构造函数时,则派生类必须定义构造函数并要使用一个或多个参数的构造函数时,则派生类必须定义构造函数并要调用基类的构造函数,提供将参数传递给基类构造函数的途径。调用基类的构造函数,提供将参数传递给基类构造函数的途径。 2派生类的构造函数与析构函数的特点派生类的构造函数与析构函数的特点7.8 虚函数虚函数 多态性就是向一个对象发出的同一个消息(即调用对象的多态性就是向一个对象发出的同一个消息(即调用对象的某个成员函数),而得到不同的操作结果。这一方面是因为类成某个成员函数),而得到不同的操作结果。这一方面是因为类成员函数的重载;另一方面是因为派生类的成员函数可以覆盖基类员函数
36、的重载;另一方面是因为派生类的成员函数可以覆盖基类的成员函数,而且的成员函数,而且C+允许定义为基类类型的指针指向派生类的允许定义为基类类型的指针指向派生类的对象,允许定义为基类类型的引用去引用其派生类对象,从而使对象,允许定义为基类类型的引用去引用其派生类对象,从而使得调用同一个成员函数名时得到不同的结果。得调用同一个成员函数名时得到不同的结果。 C+支持两种多态性:一种是编译时的多态性,称为支持两种多态性:一种是编译时的多态性,称为“静态静态绑定绑定”;另一种是运行时的多态性,称为;另一种是运行时的多态性,称为“动态绑定动态绑定”。所谓绑。所谓绑定,就是将一个函数调用链接上相应的函数体代码
37、的过程。定,就是将一个函数调用链接上相应的函数体代码的过程。7.8.1 多态性多态性 编译时的多态性就是在编译过程中决定了函数的确切调用,编译时的多态性就是在编译过程中决定了函数的确切调用,称为称为“静态绑定静态绑定”,也称为,也称为“静态联编静态联编”。 在一个类中声明的重载成员函数的静态绑定在一个类中声明的重载成员函数的静态绑定 对于一个类中的重载成员,编译系统根据重载函数的参数对于一个类中的重载成员,编译系统根据重载函数的参数个数、类型及顺序的差别,在编译时就确定了程序中函数的调用个数、类型及顺序的差别,在编译时就确定了程序中函数的调用与哪个函数绑定。与哪个函数绑定。 派生类中重载和覆盖
38、基类中的成员函数的静态绑定派生类中重载和覆盖基类中的成员函数的静态绑定 对于在派生类中覆盖基类中的成员函数,则基类的该成员对于在派生类中覆盖基类中的成员函数,则基类的该成员被编译器隐藏起来,派生类对象调用的是它定义的函数。若要在被编译器隐藏起来,派生类对象调用的是它定义的函数。若要在派生类中调用基类的被隐藏的成员,则必须使用作用域运算符派生类中调用基类的被隐藏的成员,则必须使用作用域运算符“:”,即在函数名前加上,即在函数名前加上“基类名基类名:”用来绑定基类的函数。用来绑定基类的函数。1编译时的多态性编译时的多态性 不论用类对象、对象指针或对象引用哪种形式访问成员函不论用类对象、对象指针或对
39、象引用哪种形式访问成员函数(非虚函数),编译系统都根据它们的定义类型,在编译过程数(非虚函数),编译系统都根据它们的定义类型,在编译过程中进行静态绑定。中进行静态绑定。 运行时的多态性是在程序运行时根据运行时所产生运行时的多态性是在程序运行时根据运行时所产生的信息决定调用哪个函数,它是运行过程中发生的,编的信息决定调用哪个函数,它是运行过程中发生的,编译系统在编译时是无法确定的,因此,也称为译系统在编译时是无法确定的,因此,也称为“后绑后绑定定”。运行时的多态性由虚函数来实现。运行时的多态性由虚函数来实现。2运行时的多态性运行时的多态性 虚函数(虚函数(virtual function)就是在
40、类中用保留字)就是在类中用保留字virtual声明的成员函数。在派生类中重定义所继承的成声明的成员函数。在派生类中重定义所继承的成员虚函数时,即使没有保留字员虚函数时,即使没有保留字virtual,该函数仍是虚函,该函数仍是虚函数,但为了更好地表达这些函数的实质,最好加上这个数,但为了更好地表达这些函数的实质,最好加上这个保留字。一个含有虚函数的类称为多态类,无论这些虚保留字。一个含有虚函数的类称为多态类,无论这些虚函数是继承下来的,还是在派生类中新增加的。函数是继承下来的,还是在派生类中新增加的。 一个类的虚函数仅对派生类中重定义的函数起作一个类的虚函数仅对派生类中重定义的函数起作用,对其它
41、函数没有影响。用,对其它函数没有影响。C+系统对调用虚函数进行系统对调用虚函数进行动态绑定,而普通函数则是静态绑定。动态绑定,而普通函数则是静态绑定。7.8.2 虚函数虚函数 在基类中若不给出虚函数的具体定义,而是把它的在基类中若不给出虚函数的具体定义,而是把它的定义留给派生类去完成,这时可将此虚函数声明为纯虚定义留给派生类去完成,这时可将此虚函数声明为纯虚函数。纯虚函数声明的一般形式为:函数。纯虚函数声明的一般形式为: virtual 类型类型 函数名函数名(参数列表参数列表) = 0; 包含纯虚函数的类为抽象基类。抽象基类只能用来包含纯虚函数的类为抽象基类。抽象基类只能用来派生新类,它不能
42、定义对象。派生新类,它不能定义对象。7.8.3 抽象基类抽象基类7.9 运算符重载运算符重载 重载(重载(overloaded)是)是C+的一个重要特征,不仅可以重的一个重要特征,不仅可以重载函数,还可以重载运算符。载函数,还可以重载运算符。 在在C+中,运算符实际上也是函数,只是在描述运算符函数中,运算符实际上也是函数,只是在描述运算符函数的时候,使用了一个关键字的时候,使用了一个关键字operator,而其它方面跟普通函数是,而其它方面跟普通函数是一样的。运算符重载跟函数重载一样,但是由于运算符的特殊性一样的。运算符重载跟函数重载一样,但是由于运算符的特殊性质,运算符重载也有很多函数重载不
43、具备的性质。质,运算符重载也有很多函数重载不具备的性质。 要实现用户定义类的运算符重载,要求重载运算符的函数要实现用户定义类的运算符重载,要求重载运算符的函数能访问运算对象的私有成员,所以,在能访问运算对象的私有成员,所以,在C+中有两种形式的函数中有两种形式的函数可实现运算符重载,就是成员函数或友元函数。可实现运算符重载,就是成员函数或友元函数。 7.9.1 运算符重载概述运算符重载概述用类的成员函数实现运算符重载,首先要在类中进行原型声明:用类的成员函数实现运算符重载,首先要在类中进行原型声明: 返回类型返回类型 operator 重载的运算符重载的运算符(参数类型列表参数类型列表); 然
44、后同一般成员函数一样,通常在类外给出该函数的定义:然后同一般成员函数一样,通常在类外给出该函数的定义: 返回类型返回类型 类名类名:operator 重载的运算符重载的运算符(参数列表参数列表) 相关操作;相关操作; 7.9.2 运算符重载为类的成员函数运算符重载为类的成员函数 对重载运算符的成员函数而言,其参数的个数取决于运算对重载运算符的成员函数而言,其参数的个数取决于运算符的目数。如果是单目运算符,则不需参数(个数为符的目数。如果是单目运算符,则不需参数(个数为0);如果);如果是双目运算符,则需要是双目运算符,则需要1个参数,这是由于该对象本身也作为一个参数,这是由于该对象本身也作为一
45、个操作数参与运算。个操作数参与运算。 在将单目运算符在将单目运算符+和和-重载为成员函数时,函数不需要参数。但重载为成员函数时,函数不需要参数。但是,由于单目运算符是,由于单目运算符+和和-分为先自增分为先自增/减和后自增减和后自增/减两种,为区分减两种,为区分“a+”和和“+a”,在重载后自增,在重载后自增“a+”的函数中增加的函数中增加1个参数,固定个参数,固定为为“int”。int只是作为区分先自增或后自增的标志,没有其他作用。只是作为区分先自增或后自增的标志,没有其他作用。重载运算符的函数的返回值类型常常就是我们所定义的类,所以它们重载运算符的函数的返回值类型常常就是我们所定义的类,所
46、以它们的返回值类型通常都是该类,的返回值类型通常都是该类, 7.9.2 运算符重载为类的成员函数运算符重载为类的成员函数重载先自增重载先自增+的成员函数的形式为:的成员函数的形式为: 类名类名 类名类名:operator +()() 相关操作;相关操作; 重载后自增重载后自增+的成员函数的形式为的成员函数的形式为: 类名类名 类名类名:operator +(int) 相关操作;相关操作; 可以使用友元函数重载运算符,首先要在类中声明为友元函数:可以使用友元函数重载运算符,首先要在类中声明为友元函数: friend 返回类型返回类型 operator 重载的运算符重载的运算符(参数类型列表参数类
47、型列表);然后在类外给出该函数的定义:然后在类外给出该函数的定义: 返回类型返回类型 operator 重载的运算符重载的运算符(参数列表参数列表) 相关操作;相关操作; 7.9.3 运算符重载为类的友元函数运算符重载为类的友元函数对重载运算符的友元函数而言,其参数的个数取决于运算符的目数。如果是对重载运算符的友元函数而言,其参数的个数取决于运算符的目数。如果是单目运算符,则需要单目运算符,则需要1个参数;如果是双目运算符,则需要个参数;如果是双目运算符,则需要2个参数。同样的,个参数。同样的,为区分单目运算符为区分单目运算符+和和的先自增的先自增/减和后自增减和后自增/减这两种运算,为重载后
48、自减这两种运算,为重载后自增增/减的函数增加减的函数增加1个参数个参数“int”,作为区分先自增,作为区分先自增/减或后自增减或后自增/减的标志。减的标志。C+程序中常用程序中常用cin和和cout及对应的抽取运算符及对应的抽取运算符“”和插入运算符和插入运算符“”对对标准类型的数据进行输入和输出,如果想用它们输入输出我们自定义类型的标准类型的数据进行输入和输出,如果想用它们输入输出我们自定义类型的数据,则必须进行重载。对数据,则必须进行重载。对重载的函数形式为:重载的函数形式为: istream & operator (istream &,自定义类自定义类 &); ostream & operator ”这几个与指针有关的运算符,这几个与指针有关的运算符,它们之间有一种等价关系。因此,重载它们之间有一种等价关系。因此,重载operator -( )也应该重也应该重载载operator ( )、operator &( )和和operator *( )等,以便维持这等,以便维持这种等价关系。种等价关系。7.9.4 运算符重载原则运算符重载原则