VC程序设计核心-类与对象.ppt
第3章 VC程序核心类与对象,教学要点 本章内容主要包括面向对象程序设计特点、结构和类定义、类的成员变量和成员函数定义,类的封装性在类定义中的体现,利用继承性派生新的类,类的多态性。要求了解类的各种继承方式、抽象类的概念与使用。熟悉类的定义和如何实现类的封装性、继承性和多态性 掌握构造函数和析构函数的调用特点、由类实例化对象的方法,熟练掌握通过虚函数、继承关系和指向基类的指针实现多态性的方法。,3.1 面向对象程序设计特点 3.2 面向对象的基石类3.3 对象 3.4 类的继承与派生 3.5 类的多态性,第3章目录,3.1面向对象程序设计特点,3.1.1抽象3.1.2封装3.1.3继承3.1.4多态3.1.5以VC作为面向对象程序设计的原因3.1.6*面向对象程序设计的优点,3.1.1抽象,抽象是指从具体的实例中抽取出来共同的性质并加以描述的过程,它忽略了一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。,3.1.2 封装,封装指的是将方法和数据放于同一对象中,使得对数据的存取只能通过该对象本身的方法来进行。主要思想是将数据(数据成员)及处理这些数据的相应函数(成员函数)封装到类class中(C+的一种新的定义数据类型的方法),而使用类的变量则称为对象(object),在对象内,只有属于该对象的成员函数才可以存取该对象的数据成员。这样,其他函数就不会破坏到它的内容,从而起到保护和隐藏数据的效果。,3.1.3继承,继承是一种联结类与类之间的层次模型,它允许和鼓励类的重用,提供了一种明确表达共性的方法。一个新类可以从现有的类中派生,这个过程称为类继承 继承性很好地解决了软件的可重用问题,3.1.4多态,多态性指的是同一消息被不同的对象接收后被解释为不同含义的能力,也就是一个接口名称具有多种功能。多态性语言具有语言灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名的问题。,3.1.5以VC作为面向对象程序设计的原因,1VC提供面向对象的完整语法与语义 2.VC还是一种结构化的程序设计语言3VC中的MFC是理解面向对象语法与语义 的最好平台,3.1.6面向对象程序设计的优点,面向对象编程的好处最明显的是使代码的可重用性大大提高,使程序员摆脱了重复性的劳动。面向对象编程中的对象比较独立,它给外界提供了统一的接口,当对象一旦建立,就可以重复使用。采用面向对象编程的好处就是代码的可扩充性。,3.2面向对象的基石类,3.2.1 从结构到类 3.2.2 类的定义 3.2.3 类的属性成员变量3.2.4 类的行为成员函数 3.2.5 类成员的访问控制 3.2.6*类的深入理解,3.2.1从结构到类,1结构体 VC不仅提供了大量预先定义的数据类型,而且还支持自定义的数据类型。结构就是把相互关联的一些基本数据类型的元素组成一个新的独立统一体。,【例3-1】定义一个职工Employee结构数据类型,它包括姓名、工资、地址、移动电话。struct Employeechar name9;float salary;char address40;char mobile12;,定义一个结构体,事实上是定义了一种数据类型,程序并不会给类型分配内存,内存分配是发生在结构体变量上,#include iostream.h#include string.hvoid main()struct Employee EmployeeA;strcpy(EmployeeA.name,灭绝师太);EmployeeA.salary=168.47;strcpy(EmployeeA.address,峨眉山上);cout EmployeeA.name已经领上工资了,其数目是 EmployeeA.salaryendl;cout要想拜谒她,请到 EmployeeA.address或直接传呼 EmployeeA.mobileendl;,【例3-2】定义一个具体职工EmployeeA,并对该职工进行赋值。程序的主文件名为DefStru.cpp。,程序的运行结果是:灭绝师太已经领上工资了,其数目是168.47要想拜谒她,请到峨眉山上或直接传呼,2类类是从一个个具体的事物中把共同特征抽取出来形成的一个概念,它反映了事物之间的共性 在C+中,在类的定义中,不仅有反映事物属性的成员变量,还有反映属性操作的成员函数。类一种自定义数据类型。,3.2.2类的定义,在C+中类的定义方式为:class 类名private:私有数据及成员函数;protected:保护数据及成员函数;public:公有数据及成员函数;;,class是类定义符,类名是一种标识符,它的命名规则与变量名的命名规则相同。一对花括号内是类的说明部分,说明该类的成员。类的成员包括成员变量和成员函数两部分。上述在类的成员变量和成员函数前面出现的关键字public(公有)、private(私有)或protected(保护)表示成员的访问属性。在类中声明的成员变量若不特别指明,都被视为私有类型。私有类型的数据只允许类本身声明的函数对其进行存取,而类外部的任何函数都不能访问。公有类型的成员可以被任何函数来访问,它们是类与外部的接口。,类的定义反映了对类的描述,定义一个类就是定义一种数据类型,把握类的定义首先要掌握类中包含反映事物属性的那些成员变量和反映事物操作的那些成员函数。【例3-3】定义一个职工TEmployee类,它包括姓名、工资、地址、移动电话数据成员和两个成员函数,分别实现输入职工数据和显示职工属性。,class TEmployeepublic:void input(char*name,float salary,char*address,char*mobile);void display();private:char m_name9;float m_salary;char m_address40;char m_mobile12;,【例3-4】定义一个交通工具类TVehicle,它具有轮子个数、载重两个数据成员和三个成员函数,分别实现输入交通工具的轮子和载重、取轮子个数和载重。,class TVehiclepublic:void Initialize(int wheels,float weight);/交通工具的初始化int GetWheels(void);/取轮子个数float GetWeight(void);/取载重private:int m_wheels;/轮子个数float m_weight;/载重;,3.2.3类的属性成员变量,1类的普通成员变量 类的普通成员变量指的是类的数据域的数据类型是基本数据类型如int、char、float等。例如前面定义的雇员类TEmployee,它的所有的数据域都是普通的成员变量。2类的对象成员变量 类的对象成员变量指的是类的数据域的数据类型是其它的类,这些类可以是自定义的,也可以是一些类库中的类(如后面所说的MFC类库)。,【例3-5】在前面定义的职工TEmployee类中增加一个对象成员出生时间m_Birthday,它是MFC中的一个类CTime。,class TEmployeepublic:void input(char*name,float salary,char*address,char*mobile);private:char m_name9;CTime m_birthday;/定义对象成员m_birthday;,由于CTime类是MFC中的一个类,在使用该类库中的类时,必须在类的定义文件开始加上#include 语句,并对VC当前开发的工程进行一些设置(点击菜单Projects|Setting,打开General选项卡,在Microsoft Foundation Classes列表框中选择Use MFC in a Shared DLL)。,【例3-6】看看CTime类中的对象成员,体会微软软件开发师的杰作,体会对象成员的作用(这些代码在Visual Studiovc98mfcincludeafx.h头文件中)。,class CTimepublic:/Constructorsstatic CTime PASCAL GetCurrentTime();private:time_t m_time;/类的对象成员;,3类的静态成员变量,静态成员变量则不同于一般的类的成员变量。当一个类定义多个对象时,所有对象的静态成员变量占用同一个内存空间,也就是说静态成员变量对类的所有对象只有一份,不同的对象使用相同的成员变量。应用静态数据成员实现了数据的共享,使类的各个对象中可以进行消息的传递,同时静态数据成员不会破坏数据的封装性,有利于数据的安全通信。,从形式上看,静态成员变量是在一个类的成员变量定义前加上关键词static而构成的变量;从存储角度上看,静态成员变量在整个类上只保留一份,即该类的任何对象都共享该静态成员变量;从作用域来看,静态成员变量可看作类的公共变量;静态成员变量属于一个类而不属于该类的任何对象,所以,在类外调用静态成员变量时,必须要用“类名:”作为限定词。,【例3-7】一个含有类的静态成员变量的实例:某班级有三个同学A、B、C,该班有班费1000元。每个人都可以访问班级的班费,当A花去班费中的钱时,B、C得到的班费就少了,同样,当B花去班费中的钱时,A、C得到的班费就少了,因而应将班费设置为静态成员变量。它的特点是该类的每个对象都可以访问,属于类的公共变量。,class TStudentprivate:char m_Name6;static int m_ClassMoney;/m_ClassMoney为静态成员变量,保存班费public:void InitStudent(char*);void ExpendMoney(int);static void ShowMoney();int TStudent:m_ClassMoney=1000;/静态成员变量的初始化,【例3-8】了解应用程序类CWinApp公有的成员变量,CwinApp是Windows的一个应用程序类,它里面的一些公有的成员变量在程序中经常要使用。m_pszAppName:应用程序的名称(包含路径)。m_hInstance:应用程序的当前实例。m_hPrevInstance:应用程序的前一个实例。m_nCmdShow:应用程序主窗口的最初何显示方式(最大化、最小化等)。m_pMainWnd:指向应用程序主窗口的指针变量。m_pszExeName:应用程序的模块名称(不包含路径)。,3.2.4类的行为成员函数,当成员函数的函数体代码较短时,可以在定义类时直接定义成员函数,即在类内定义成员函数。但当成员函数函数体较复杂时,可以在类体外定义成员函数,但必须在类内对成员函数进行声明。,1类的普通成员函数,【例3-9】在前面定义的职工TEmployee类中有一个输入职工姓名、工资等属性的成员函数input(),通过它实现职工对象的初始化。另外还有一个显示职工信息的成员函数diplay(),这些函数在类体外进行定义:,class TEmployeepublic:void input(char*name,float salary,char*address,char*mobile);void display();private:char m_name9;void TEmployee:input(char*name,float salary,char*address,char*mobile)strcpy(m_name,name);/字符串赋值必须通过字符串拷贝来实现m_salary=salary;strcpy(m_address,address);strcpy(m_mobile,mobile);,void TEmployee:display()coutName Is m_nameendl;coutSalary Is m_salaryendl;coutAddress Is m_addressendl;coutmobile Is m_mobileendl;,说明:在类体外定义成员函数时必须使用作用域运算符“:”来指明成员函数所属的类,在类体内访问成员函数时,若未指明所属的类,则默认是这个类的成员函数,若要访问该类之外的其它类的成员函数,则必须指名该成员函数所属的类。,2类的静态成员函数,静态成员函数仅属于一个类而不属于该类的任何对象,因此它不通过对象直接调用,而是与类相联系的。静态成员函数中没有this指针,并且静态成员函数中只能访问静态成员变量,在类外调用静态成员函数时应使用以下方式:类名:静态成员函数名(参数列表);,【例3-10】一个含有静态成员函数的其使用。程序的主文件为StaticMember.cpp。,#include iostream.h#include class TStudentprivate:char m_Name6;static int m_ClassMoney;/m_ClassMoney为静态成员变量,保存班费public:void InitStudent(char*);void ExpendMoney(int);static void ShowMoney();/静态成员函数;,int TStudent:m_ClassMoney=1000;/静态成员变量的初始化void TStudent:InitStudent(char name)strcpy(m_Name,name);void TStudent:ExpendMoney(int money)m_ClassMoney-=money;/班费为原先的减去花费的 void TStudent:ShowMoney()cout班费还剩余 m_ClassMoneyendl;,void main()TStudent stu3;/定义三个学生stu0.InitStudent(marry);stu1.InitStudent(sunny);stu2.InitStudent(jahn);stu0.ExpendMoney(100);stu0.ShowMoney();/作用等同于TStudent:ShowMoney();stu1.ExpendMoney(100);stu1.ShowMoney();/作用等同于TStudent:ShowMoney();stu2.ExpendMoney(100);stu2.ShowMoney();/作用等同于TStudent:ShowMoney();,3类的虚成员函数,从形式上看,虚函数是在一个函数的定义前面加上关键词virtual而构成的函数。将函数定义为虚函数后,可以达到动态联编的效果,【例3-11】一个含有虚函数的类。,class TPoint/定义一个点类private:float x,y;/x为横坐标,y为纵坐标public:void Point(float i,float j)x=i;y=j;/构造函数virtual float area()return 0.0;/求面积的虚函数;,4MFC中类CString的常见操作,下面以在第二篇中常用的一个字符串类CString为例来说明类的成员函数功能。,CString的主要成员函数如下:(1)GetLength:返回CString对象中的字符数。(2)IsEmpty:测试一个CString对象是否空。(3)Empty:将一个字符数的长度强制为0。(4)GetAt:返回给定位置上的字符。(5)SetAt:设置给定位置上的字符。(6)Mid:提取一个字符串的中间部分。,请看下例:,(7)Left:提取一个字符串的左边部分。(8)Right:提取一个字符串的右边部分。(9)MakeUpper:将字符串中的所有字符转换为大写字符。(10)MakeLower:将字符串中的所有字符转换为小写字符。(11)Find:在一个较大的字符串中查找某一个字符串。(12)GetBuffer:返回一个指向CString对象的指针。,#include iostream.h#include afx.h/CString的头文件void main()CString Str1=VC+实用教程;CString Str2=出版商:电子工业出版社n;CString Str3=Str1+Str2;CString Str4=出版社;coutStr3.GetBuffer(0);/取字符串的首地址,【例3-12】CString的使用实例。该程序的主文件为StringTest.cpp,比较,c*c*c,if(Str1.Compare(Str2)/字符串比较cout0)/利用成员函数Find查找子串cout在Str2中找到子串Str4n;,程序运行结果为:VC+实用教程出版商:电子工业出版社字符串Str1与Str2不相同VC教程在Str2中找到子串Str4,3.2.5类成员的访问控制,类的主要作用是实现对数据和操作的封装 对类的成员访问必须有所限制,根据访问权限不同可将类的访问控制分为公有(public)、受保护(protected)和私有(private)三种方式 如果未加说明,则默认的访问控制为private。,请看下例:,【例3-13】访问控制实例。该程序的主文件为AccessControl.cpp,#include#define PI 3.14159class circlepublic:void set(int r)radius=r;float area();private:float radius;float circle:area()return(PI*radius*radius);,void main()int radius;circle MyCircle;coutradius;MyCircle.set(radius);/正确,因为成员函数set()为公有成员cout该圆的面积为:MyCircle.area()endl;/正确,因为成员函数area()为公有成员/c.radius=3.3;/错误,类不能对私有成员直接进行访问,类的访问控制实质上反映了类对其成员的封装程度类的访问控制实质上反映了类对其成员的封装程度,当成员被声明为public时,它可以被外部类的对象访问,也可以被派生类的对象访问;当成员被声明为protected时,它不能被外部类的对象访问,但可以被派生类的对象访问;当成员被声明为private时,它既不能被外部类的对象访问,也不能被派生类的对象访问,只能被本类的成员函数访问。不管哪一种访问类型定义的成员,都可以被它所属类的成员函数所访问。,3.2.6*类的深入理解,1类是一种数据类型2类是一个代数系统 3类是实现面向对象特性的载体,3.3 对象,3.3.1 对象的声明 3.3.2 对象的使用3.3.3 对象的初始化构造函数3.3.4 对象的撤消析构函数*对象的深入理解,3.3.1对象的声明,int i;/声明一个整型对象i,该对象占有32个字节的空间 char ch;/声明一个字符对象ch,该对象占有1个字节的空间 CTime MyTime;/声明一个日期时间对象MyTimeCPoint MyPoint;/声明个点对象MyPointCRect MyRect;/声明一个矩形对象MyRecct 其中CTime、CPoint、CRect是VC中MFC类库中的一些通用类,这些类在实际设计程序时有着很重要的作用。,3.3.2对象的使用,当声明了对象后,就可以使用该对象所隶属的类的公有(在public部分定义的)成员变量和成员函数。对象的使用,关键是使用对象所属类的成员函数,即熟悉该类都有哪些常用的成员函数。,【例3-15】建立一个日期类,并且主函数中使用其公有的成员函。主文件为DefObject.cpp,#include class TDatepublic:void SetDate(int year,int month,int day)m_year=year;m_month=month;m_day=day;void ShowDate()cout The date is m_year-m_month-m_dayendl;private:int m_year;int m_month;int m_day;,void main()TDate Today;/建立一个日期对象 Today.SetDate(2004,8,1);/调用设置日期的函数Today.ShowDate();/调用显示日期的函数 程序运行结果为:The date is 2004-8-1,3.3.3对象的初始化构造函数,构造函数是系统在创建对象时进行初始化的工具 构造函数是类的一种特殊的成员函数 其特殊性在于:,(1)功能特殊:构造函数是为新创建的对象分配内存空间或为数据成员赋初值,一般在创建对象时,为对象做初始化的工作。(2)形式特殊:构造函数的名称和类的名字相同,并且该函数无函数类型的说明;(3)调用时机特殊:构造函数不像普通成员函数那样需要显式调用(对象.成员函数的形式或对象指针-成员函数),它是隐式调用,即在一个新对象建立时(包含两种情况:一种是在对象的声明语句中,另一种是用new函数建立新的动态对象时),该对象所隶属类的构造函数自动被调用。,注:,当类中不给出类的构造函数时,系统将自动给出默认的构造函数,这个构造函数没有参数,而且函数体为空 构造函数不允许有返回值,也不许定义函数的返回值类型 构造函数可以重载,即可以定义多个参数个数不同或类型不同的构造函数。,3.3.4对象的撤消析构函数,析构函数是在撤消对象时进行收尾工作的工具。它的功能是:在对象被撤消时,对该对象所占的空间进行释放。当类中没有给出析构函数时,系统将自动生成一个缺省的构造函数。当撤消对象时(对象的生存期结束或通过delete函数释放动态对象),系统会自动调用析构函数。析构函数不允许定义函数的返回值类型,定义析构函数时不能指定任何形式的参数,而且析构函数也不能重载,即析构函数在一个类中只能有一个。,【例3-16】一个包含构造函数和析构函数的例子。程序的主文件为ConstructorExam.cpp。,#include#include class Exlprivate:int x;public:Exl()coutEx1 类的对象建立,其构造函数被调用n;Exl()coutEx1类的对象撤消,其析构函数被调用n;void setdata(int i)x=i*2;/在类中定义成员函数setdata()void print();/类Exl定义结束,class Ex2int y;public:Ex2()coutEx2 类的对象建立,其构造函数被调用n;Ex2()coutEx2类的对象撤消,其析构函数被调用n;void setdata(int j)y=j*j;void print();/类Ex2定义结束void Ex2:print()coutclass Ex2:setw(6)y n;/成员函数print()定义结束,void main()Exl s;Ex2 t;s.setdata(5);s.print();t.setdata(5);t.print();,程序运行结果为:Ex1 类的对象建立,其构造函数被调用Ex2 类的对象建立,其构造函数被调用class Exl:10class Ex2:25Ex2类的对象撤消,其析构函数被调用Ex1类的对象撤消,其析构函数被调用,3.3.5*对象的深入理解,1对象就是变量 2对象是封装了属性和行为的基本单位 3对象是一个有限状态自动机,3.4 类的继承与派生,3.4.1 类与类之间的四种关系3.4.2 继承的概念 3.4.3 派生类的声明 3.4.4 基类成员的访问控制 3.4.5 派生与继承的应用,3.4.1 类与类之间的四种关系,是封装了事物属性和方法的同类对象的集合。,类,UML(unified model language,统一建模语言)认为,类之间主要存在四种关系。,1关联(Association)关系,关联关系表示两个类之间存在某种语义上的联系,即与该关联连接的类的对象之间具有一定的语义连接关系,该关系表达了类之间的一种相关性。,2依赖(Dependency)关系,依赖关系描述的是两个类之间的语义上的连接关系,它是一种“Use-A”关系。假设有两个元素A与B,如果修改元素A的定义可能会引起对另一个元素B的定义的修改,则称元素B依赖于A。,3聚合(Aggregation)关系,聚合关系是一种“Has-A”关系,它体现的是类之间的一种整体与部分的关系。例如汽车包括四个轮子和一个发动机等。,4泛化(Generalization)关系,泛化关系是一种“Is-A”关系,它描述的是类之间的“一般”与“特殊”的关系。具有共同特性的元素可以抽象为一般类,并通过增加其内涵,进一步抽象成特殊类。该关系可以将类组成一种有层次、有分类的结构。例如:狗是一种哺乳动物,哺乳动物是一种动物等。在VC中建立类之间的泛化关系采用的是继承机制。,3.4.2 继承的概念,继承是描述类与类之间关系的一个概念,它提高了代码的重用性,使得程序更加模块化,便于程序更简单而又准确地描述事物。继承关系可以用由派生类指向基类的箭头表示,如图3.4所示:,3.4.3 派生类的声明,单继承中派生类的声明格式为:class 派生类类名:访问控制限定符 基类名;其中访问控制限定符号可以是public(公有派生)、protected(保护派生)和private(私有派生)。,【例3-17】一个派生类的声明实例。如果交通工具用前面讲的类TVehicle来抽象表示,汽车用类TCar来抽象表示的话,那么汽车从交通工具继承而来(或交通工具派生汽车的)的语义表示成C+语言的语法描述为:,class TCar:public TVehiclepublic:void Initialize(int wheels,float weight,int passenger=4);GetPassenger(void);private:int m_passenger;,3.4.4 基类成员的访问控制,派生类对基类成员的访问不仅取决于基类成员声明的访问控属性,还取决于派生类对基类的“继承程度”。继承程度有有三种:公有(public)、受保护(protected)和私有(private)类型的继承。,我们可以根据下表中的继承成员访问控制规则来决定使用哪种访问方式更合理,不同的访问控制,决定了基类可以被派生类所访问的“开放”程度。,表3.1 继承成员访问控制规则,【例3-19】一个使用类的继承实例。主文件为InheritTest.cpp。,3.4.5 派生与继承的应用,#include iostream.hclass TVehicle/交通工具类的声明public:void Initialize(int wheels,float weight);int GetWheels(void)return m_wheels;float GetWeight(void)return m_weight;private:int m_wheels;/轮子float m_weight;/载重;,void TVehicle:Initialize(int wheels,float weight)m_wheels=wheels;m_weight=weight;class TCar:public TVehicle/汽车类从交通工具类公有派生public:void Initialize(int wheels,float weight,int passenger);int GetPassenger(void)return m_passenger;private:int m_passenger;/乘客数;void TCar:Initialize(int wheels,float weight,int passenger=4)TVehicle:Initialize(wheels,weight);/调用父类的函数进行初始化,void main()TVehicle VehicleInstance;/声明一个交通工具对象VehicleInstanceTCar CarInstance;/声明一个汽车对象CarInstanceVehicleInstance.Initialize(4,6);/初始化交通工具cout交通工具轮子的个数是:VehicleInstance.GetWheels()endl;cout交通工具轮子的载重为:VehicleInstance.GetWeight()endl;CarInstance.Initialize(1,2);/初始化汽车/调用从基类继承而来的函数GetWheels等 cout汽车的轮子、载重和乘客数为:m_passenger=passenger;CarInstance.GetWheels()tCarInstance.GetWeight()t;coutCarInstance.GetPassenger()endl;/调用派生类新增的函数GetPassenger,程序运行结果为:交通工具轮子的个数是:4交通工具轮子的载重为:6汽车的轮子、载重和乘客数为:1 2 4,3.5 类的多态性,3.5.1 多态性概述3.5.2 多态性的类型 3.5.3 编译时多态性的两种方式3.5.4 运行时多态性的实现条件3.5.5 纯虚函数与抽象类 3.5.6*使用虚函数的好处习题,3.5.1 多态性概述,在VC中,这种同一符号或名字在不同情况下表现为不同的语义现象称为多态性。,严格地讲,在VC中,不同的类可以有语义相同的函数,这些函数在概念上是相似的,但对应各自的类来说,其实现是不同的,因而对象在接收到同样的函数调用时所引发的行为有可能是不同的,这一功能称为多态性。直观地讲就是“一个名称,多个函数”。,3.5.2 多态性的类型,程序设计的多态性有两种形式:编译时的多态性和运行时的多态性。,多态性的实现与联编有关。将一个函数的调用与其相应的函数体代码相链接的过程,称为函数联编。C+中有两种类型的联编:静态联编和动态联编。,静态联编是指在调用同名函数(即重载函数)时,编译器将根据调用时所使用的实际参数个数、类型的不同确定应该调用哪一个函数的实现,它是在程序编译阶段就确定下来的多态性。静态联编通过使用重载(overload)机制来获得,重载机制包括函数重载和运算符重载。,动态联编是在程序的运行阶段根据当时的情况来确定应该调用哪个同名函数的实现。动态联编主要通过继承与虚函数两者结合来实现的。,静态联编的代码链接工作是在编译时完成的,所以运行时不需要额外时间来做这些工作,因此静态联编的代码效率较高。动态联编将函数调用的链接时间后移到代码执行的时候,这必然使函数调用时间开销增加,效率降低,但大大增强了语言的灵活性。程序员可以根据实际情况需要来决定使用哪一种联编方式,从而兼顾代码的高效性和灵活性。,静态联编与动态联编各有优缺点:,3.5.3 编译时多态性的两种形式-函数与运算符重载,1函数重载 两个以上的函数,取相同的函数名称,但是形参个数或者类型不同,编译器根据实参和形参的类型和个数选择最匹配的函数,来自动地确定调用哪一个函数,这就是函数的重载。函数重载包括普通成员函数重载和类的成员函数重载,下面给出相应的实例。,【例3-20】普通函数重载的例子。给函数add()定义多个函数来实现,该函数的功能是求和,可以求两个整型数之和,两个浮点数之和,也可以求三个双精度浮点数之和,每种实现对应着一个函数体,这些函数的名字相同,但是函数的参数个数或类型不同,这就是函数重载。在VC+中,函数可以共享同一个名字,只是这些同名函数的参数表有所不同。程序的主文件为Function.cpp。,#includeint add(int a,int b)return a+b;float add(float x,float y)return x+y;void add(double a,double b,double c)couta+b+cendl;void main()int m=1,n=2;float x=1.2,y=2.3;double a=2.0,b=3.0,c=2.5;coutadd(m,n)endl;/因为m与n为int型,故调用的是第一个add函数 coutadd(x,y)endl;/因为x与y为float型,故调用的是第二个add函数 add(a,b,c);/因为a、b、c为double,且包含三个参数型,故调用的是第三 个add函数,从程序的运行结果来看add(m,n)是调用了int add(int a,int b)函数;add(x,y)调用了函数float add(float x,float y);add(a,b,c)调用了void add(double a,double b,double c)函数。这些函数可以同名,同名的函数为重载函数。,程序的运行结果是:33.57.5,注意:(1)函数重载时,函数声明必须能互相区别,即函数参数个数或参数类型必须有所不同,如果只有函数的返回值不同,不能区分重载函数;(2)函数功能一般相类似,本质上有区别的函数,最好不要重载;(3)不同参数传递方式无法区别重载函数。例如:int add(int a,int b);int add(int 这两个函数不能作为重载函数,编译时会出错。,【例3-21】类的成员函数重载的例子。类的成员函数重载的最典型的情况在类的构造函数的重载。MFC类库中有一个点类CPoint,查看MSDN的说明,知道它有多个构造函数,下面为其中的构造函数的原型说明:CPoint(int initX,int initY);CPoint(POINT initPt);CPoint(SIZE initSize);,2运算符重载,运算符重载本质上就是函数的重载,是函数重载的特殊形式(函数名为运算符号),VC语言中允许程序员重新定义运算符的语义,这一机制称为运算符重载。,与函数重载类似,运算符重载就是当运算符的操作对象不同时,运算符呈现出不同的运算状态。运算符函数有两种形式:一种是在类中定义的运算符成员函数,即类成员运算符;另一种是在类之外定义的运算符函数,通常使用友元形式,即友元运算符。,类的运算符重载的定义形式类型 类名:operator重载运算符(参数表)/运算符函数体,【例3-22】用类的成员函数重载运算符“+”实现复数相加的例子,重载运算符“=”实现两个复数是否相等的判断。程序的主文件为Operator.cpp,#include iostream.hclass TComplex/定义数学上的复数类public:TComplex(float real=0,float image=0);/缺省构造函数TComplex(const TComplex,TComplex:TComplex(float real,float image)/缺省的构造函数的实现m_real=real;m_image=image;TComplex:TComplex(const TComplex,void TComplex:ShowValue()/显示复数值代码 cout0)cout+m_imagei;if(m_image0)coutm_imagei;coutendl;void main()TComplex Complex1(6,8);/将复数Complex1设置为4+5iTComplex Complex2(-2,-3);/将复数Complex1设置为-2-3i,调用缺省构造函数完成TComplex Complex3(Complex1);/用Complex1初始化Complex3,调用拷贝构造函数完成后,这两个复数相等TComplex Complex4,Complex5;/将复数Complex4,Complex5设置为0,调用缺省构造函数完成,Complex1.ShowValue();/显示复数complex1Complex2.ShowValue();Complex3.ShowValue();Comple