




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第3章类和对象本章介绍类与对象的定义、成员函数、构造函数、析构函数、对象与指针等基本概念及应用,讲解C++的继承、重载、多太性等特性,使读者掌握C++的编程基本知识。第3章类和对象本章介绍类与对象的定义、成员13.1类与对象的定义3.2成员函数3.3构造函数3.4析构函数3.5对象与指针3.6类与结构3.7继承3.8多重继承3.9派生类的对象和构造函数3.10多态性3.11重载3.12虚拟函数实验3类和对象、继承和派生3.1类与对象的定义21.类的定义类定义的语法如下:class类名{private:数据成员或成员函数;public:数据成员或成员函数;protected:数据成员或成员函数;};
3.1类与对象的定义类的定义由两大部分组成,即类的数据成员和成员函数。类的数据成员和成员函数也称为类的属性和类的操作。数据成员类似C结构中的变量,成员函数定义了类对数据的操作,反映了类行为和类的能力。1.类的定义3.1类与对象的定义类的定义由两大部3例3.1:定义一个student类,用以描述学生对象。
123例3.1:定义一个student类,用以描述学生对象。4
classStudent{private:charm_Name[20];intm_Age;intm_Sex;charm_cl[20];intm_number;public:voidRegister(char*Name,intAge,charSex,char*cl,intnumber);voidGetName(char*Name);intGetAge();charGetSex();voidGetcl(char*cl);intGetnumber();};程序实现:
52.对象的定义对象是类的实例。从技术上讲,一个对象就是一个具有某种类类型的变量。与普通变量一样,对象也必须先经声明才可以使用。
声明一个对象的一般形式为:<类名><对象1>,<对象2>,…例:StudentStudent1,Student2;声明了两个名为Student1和Student2的Student类的对象。2.对象的定义对象是类的实例。从技术上讲,一6
在程序中使用一个对象,通常是通过对体现对象特征的数据成员的操作实现的。当然,由于封装性的要求,这些操作又是通过对象的成员函数实现的。具体来说:(1)成员函数访问同类中的数据成员,或调用同类中的其他成员函数,可直接使用数据成员名或成员函数名;(2)在对象外访问其数据成员或成员函数需使用运算符“.”访问对象的成员,例如:number=Student1.Getnumber();(3)不能直接访问一个对象中的私有成员,如果直接访问则导致语法错误;(4)同类对象之间可以整体赋值。例如:Student2=Student1;(5)对象用作函数的参数时属于赋值调用;函数可以返回一个对象。在程序中使用一个73.2成员函数在类的定义中仅给出了成员函数的原型,函数的定义还需在其他地方(通常每个类的成员函数的定义放在一起构成一个源程序文件)给出。类的成员函数的一般形式为:<类型><类名>::函数名(<参数表>){‹函数体›}其中作用域运算符“::”指出成员函数的类属。没有类属的函数称为公共函数。
3.2成员函数在类的定义中仅给出了成员函8例3.2.1类成员函数的定义。说明:按例3-1说明的方法添加头文件Student.h,输入Student类的定义。然后为项目添加一源代码文件Student.cpp//Example(Student.cpp):Student类成员函数的定义#include<string.h>#include“Student.h”voidStudent::Register(char*Name,intAge,charSex,char*class,intnumber);{strcpy(m_Name,name);m_Age=age;m_Sex=(sex==’m’?0:1);strcpy((m_cl,cl);m_number=number;}
例3.2.1类成员函数的定义。说明:按例3-1说明的方9voidStudent::GetName(char*name){strcpy(name,m_Name);}intStudent::GetAge(){returnm_Age;}charStudent::GetSex(){return(m_Sex==0?‘m’:‘f’):}voidStudent::Getcl(char*cl){strcpy(cl,m_cl);}intStudent::Getnumber(){returnm_number}voidStudent::GetName(char*10例3.2.2定义Student类,并将类Student的成员函数声明为内联函数
。clStudent{private:charm_Name[20];intm_Age;intm_Sex;charm_cl[20];intm_number;public:voidRegister(char*Name,intAge,charSex,char*cl,intnumber){strcpy(m_Name,name);m_Age=age;m_Sex=(sex==’m’?0:1);strcpy((m_cl,cl);m_number=number;}例3.2.2定义Student类,并将类Student的成11voidGetName(char*Name){strcpy(name,m_Name);}intGetAge(){returnm_Age;}charGetSex(){return(m_Sex==0?‘m’:‘f’);}voidGetcl(char*cl){strcpy(cl,m_cl);}intGetnumber(){returnm_number}};voidGetName(char*Name)12例3.2.3学生资料的输入输出。//Example:学生资料的输入和输出#include<iostream.h>#voidmain(){voidOutStudentData(Student);charname[20],sex;intage;charcl[20];intnumber;StudentStudent1,Studen2;cout<<“EnteraStudent’sname、age、sex、cland.number:”;cin>>name>>age>>sex>>cl>>number;Student1.Register(name,age,sex,cl,number);Student2.Register(“Zhang2”,19,’m’,“9901”,100);cout<<“student1:\t”;OutStudentData(Student1);cout<<“Student2:\t”;OutStudentData(Student2);Student2=Student1;Cout<<“Student3:\t”;OutStudentData(Student2);}例3.2.3学生资料的输入输出。//Example:学13voidOutStudentData(Student);{charname[20];charcl[20];Student.GetName(name);Student.GetCl(cl)cout<<name<<“\t”<<Student.GetAge()<<“\t”<<Student.GetSex()<<“\t”<<cl<<“\t”<<Student.Getnumber()<<end1;}EnteraStudent’sname、age、sex、clandnumber:zhang120’f’“9901”101输入:Student1:zhang120f9901101输出EnteraStudent’sname、age、sex、clandnumber:zhang219’m’“9902”100输入:Student2:zhang219m9902100输出EnteraStudent’sname、age、sex、clandnumber:zhang320’f’“9901”102输入:Student3:zhang320f9901102输出voidOutStudentData(Student);E143.3构造函数构造函数是与类名相同的特殊成员函数,当定义对象或者用new动态生成对象时,编译程序将自动调用构造函数以实现对象的初始化。构造函数与普通成员函数的区别,是不允许指定返回类型,也不返回值。构造函数允许重载。构造函数的声明格式为:<类名>(<参数表>);3.3构造函数构造函数是与类名相同的特殊成15例3.3.1为类Student增加构造函数。//Example:为类Student增加构造函数#include<string.h>classStudent{Private:charm_Name[20];intm_Age;intm_Sex;charm_cl[20];intm_number;Public:Student(constchar*name,intage,charsex,constcharcl,intnumber)
例3.3.1为类Student增加构造函数。//Exa16//构造函数{strcpy(m_Name,name);m_Age=age;m_Sex=(sex=’m’?0:1);strcpy(m_cl,cl);m_number=number;}voidRegister(char*Name,intAge,charSex,char*cl,intnumber);voidGetName(char*Name);intGetAge();charGetSex();voidGetcl(char*cl);intGetnumber();};说明:如果在类中没有声明任何构造函数的话,系统会自动地为它定义一个形如<类名>::<类名>()的缺省构造函数,这种不带任何参数且函数体为空的构造函数就叫做缺省的构造函数。如果在类声明中已经定义了构造函数,则系统不再提供缺省的构造函数。//构造函数说明:如果在类中没有声明任何构造函数的话,系统会173.4析构函数析构函数的函数名是在类名前加波浪名“~”的特殊成员函数。析构函数与构造函数的功能正好相反;构造函数在对象生成时为其他分配存储空间,而析构函数的功能是在对象销毁时释放它所占用的内存。析构函数与构造函数一样没有返回值,但它不带参数,不能被重载。
析构函数的声明方法为:classExample{public:~Example();//析构函数;};
当类的声明中没有定义析构函数时,C++编译程序会自动产生一个默认的析构函数。。
3.4析构函数析构函数的函数名是在类名前18例:#include<iostream.h>classExample{public:Example();~Example();};Example(){cout<<“Objectiscreated!”<<\\n;}Example::~Example(){cout<<“Objectisdestroyed!”<<endl;}voidmain(){ExampleEx;}Objectiscreated!Objectisdestroyed!执行结果例:#include<iostream.h>Object193.5对象与指针可以声明指向对象的指针,方法与声明指向变量的指针相同。例如:
StudentStudent1(“Zhang”,19,’f’);Student*ptr=&Student1;通过指针访问对象的成员要用运算符“->”,例如:
Ptr->GetAge();当对象接收到一个消息时,便会调用相应的成员函数。这时,系统会向该成员函数传递一个隐含的参数,即指向该对象自身的指针,即this指针。一般来说,this指针用途不大,因为在成员函数中可以直接使用本对象内部的所有成员变量和成员函数。但在某些场合中调用其他类的成员函数时,可能需用要传送本对象的地址,在Windows程序设计中这种情况很多,这时就要用到this指针。
3.5对象与指针可以声明指向对象的指针,203.6类与结构C++中有一个结构体类型,其定义和使用方法与类非常相似。
结构体类型的定义方法如下:Struct<结构体类型名>{<结构体类型的成员变量声明语句表>};3.6类与结构C++中有一个结构体类型,21例:可定义一个表示日期的结构体类型。structdate{intda_year;intda_mon;intda_day;};即一个日期类型的变量有3个成员变量:年份(da_year)、月份(da_mon)和日(da_day)。例:可定义一个表示日期的结构体类型。structdate22在定义好结构体类型以后,就可以定义该类型的变量了:
struct<结构体类型名><变量表>;注意在定义时可以略去结构体类型说明符struct,这一点与C语言不同。例:变量声明语句。dateyesterday,today,tomorrow;声明了3个日期类型的变量:yesterday、today和tomorrow。
结构体类型的变量可用作函数的参数或者函数的返回值。
对结构体类型变量的成员变量的引用方法与类相同:<结构体类型变量名>.<成员变量名>例:today.da_year=2001;today.da_mon=10;today.da_day=10;在定义好结构体类型以后,就可以定义该类型的变量了233.7继承继承性是面向对象程序设计语言的主要特征之一。继承性指的是一个新类可以从现有的类中派生出来,继承该类的特性。被继承的类称为基类(baseclass)或父类(parentclass),继承的类称为派生类(derivedclass)或子类(childclass)。继承性允许建立类层次结构,是程序中代码可复用性的关键,并提供对多态性的支持,是C++面向对象程序设计的基石。3.7继承继承性是面向对象程序设计语言的主243.7继承1.继承与类层次结构生物动物植物微生物爬行动物哺乳动物鸟类猫科动物猫图人类认识事物的抽象层次3.7继承1.继承与类层次结构生物动物植物微生物爬行动25在软件设计中,人们模拟这一过程。在面向对象的程序设计中,继承提供了重用已有代码生成新类的方法。继承在类之间建立了关系。子类可以继承父类的所有数据结构和操作,只要在子类中添加父类没有的数据结构和操作。在上图中,对象类型CButton是从CWnd派生出来的,CWnd是从CCmdTarget派生出来的,而CCmdTarget又是从CObject派生出来的。CObjectCCmdTargetCByteArrayCCmdTargetCframeWndCDialogCButton在软件设计中,人们模拟这一过程。在面向对象的程序设计中262.派生类的工作方式
在C++程序中,当一个类是从一个基类派生出来的时候,该派生类就继承了基类所有的成员变量和成员函数,而且可以加入自己的成员变量和成员函数。但是,基类中声明为私有的成员变量和成员函数是不可为派生类访问的。
基类变量1变量2函数1函数2派生类其他变量其他函数图说明基类和派生类之间的层次关系2.派生类的工作方式基类派生类图说明基类和派生类之27
3.派生一个类声明一个派生类的语法形式为:
class派生类名称:继承权限基类名称{┇};其中,继承权限说明符有三种:public、private和protected,默认权限为private。在类的定义中,所有公有成员都允许其他函数访问,而私有成员则只能供成员函数访问。但是,当派生类继承基类的特性时,派生类无法访问基类中的私有成员。若想让基类中的某些成员不仅可供成员函数访问,而且可将其特性传给派生类,那么应将其声明为保护类型。
28
例:类继承的例子
。#include<iostream.h>classparent //声明基类{inti;//私有成员变量i仅供成员函数使用,不能传递给派生类protected: //受保护成员变量仅供成员函数使用,可供派生类使用 intx;public: //公有成员,可供任何函数使用parent();voidchange();voidshow();};parent::parent() //定义构造函数{ x=0; i=0;}voidparent::change() //定义成员函数{ x++; i++;}
29voidparent::show() //定义成员函数{ cout<<”x=”<<x<<”\n”;}classson:publicparent//声明son为parent的派生类,继承权限为public,即parent类的//公有成员在son中仍为公有成员,parent类的保护成员在son中仍为//保护成员{public: voidincrease(); //声明成员函数};voidson::increase() //定义成员函数体{ x++;}保护成员变量x公有成员函数parent公有成员函数change公有成员函数increase公有成员函数show
经过类继承,派生类son访问以下成员voidparent::show() //定义成员函数30
例某公司使用EMPINFO程序管理自己的雇员信息。后来,当该公司在国外开了分支机构而且雇佣不止一个国家的雇员时,如何重新设计EMPINFO程序呢?提示:为了满足跨国公司的需要,程序设计者从EMPINFO程序中使用的EmpInfo类派生了一个新类,名为OffshoreEmpInfo。#include<iostream.h>classEmpInfo //基类{public:EmpInfo(){} //构造函数和析构函数~EmpInfo(){}private: char*m_name; char*m_dept;char*m_position;longm_salary;例某公司使用EMPINFO程序管理自31public: voidSetName(char*name){m_name=name;}//设置员变量的值 voidSetDept(char*dept){m_dept=dept;} voidSetPosition(char*position) {m_position=position;} SetSalary(longsalary) {m_salary=salary;} voidPrintInfo();};classOffshoreEmpInfo:publicEmpInfo //派生类{public: OffshoreEmpInfo(){} //构造函数和析构函数 ~OffshoreEmpInfo(){} private: char*m_country;public: voidSetCountry(char*country) {m_country=country;}voidPintInfo();};public:32voidEmpInfo::PrintInfo(){ cout<<”Name:”<<m_name<<”\n”; cout<<”Department:”<<m_dept<<”\n”; cout<<”Position:”<<m_position<<”\n”; cout<<”Salary:”<<m_salary<<”\n”;}voidOffshoreEmpInfo::PrintInfo(){ EmpInfo::PrintInfo(); Cout<<“Country:”<<m_Country<<“\n”;}intmain(){OffshoreEmpInfoempInfo; //定义类OffshoreEmpInfo的对象empInfo.SetName(“PaulNorton”);empInfo.SetDept(“Development”);empInfo.SetPosition(“Engineer”);empInfo.SetSalary(24000);empInfo.SetCountry(“NewZealand”);empInfo.PrintInfo();return0;}Name:PaulNortonDepartment:DevelopmentPosition:EngineerSalary:24000Country:NewZealand输出voidEmpInfo::PrintInfo()Name:333.8多重继承基类与派生类之间可以有复杂的多对多关系。在类继承中,允许某个类同时继承多个基类,即所谓类的多重继承。在类的多重继承中,基类和派生类间是多对一的关系。基类1基类2基类n……派生类继承
继承
继承
继承
3.8多重继承基类与派生类之间可以有复杂的多对34
通过多重继承,派生类将继承多个基类的特性,此时基类和派生类的声明方式为:
classN1 //声明基类N1{┇};┇classNm //声明基类Nm{┇};classS:publicN1,…,publicNm //声明派生类S同时继承基类N1,…,基类Nm{┇};注意:继承一个以上的基类,要使用逗号分隔的表,还要确保为每个被继承的基类使用一个继承权限说明符。通过多重继承,派生类将继承多个基类的特性,此35例:两用沙发是一个沙发,也是一张床,两用沙发同时具有沙发和床的特性。程序中类SleeperSofa继承Bed和Sofa两个类。#include<iostream.h>classSofa{protected: intlength;public: Sofa():length(0){} voidSitOn() {cout<<"Sitting...\n";}voidSetLength(inti){length=i;}};classBed{protected: intlength;public: Bed():length(0){} voidSleep(){cout<<"Sleeping...\n";}voidSetLength(inti){length=i;}};例:两用沙发是一个沙发,也是一张床,两用沙发同时具有沙发和床36classSleepSofa:publicSofa,publicBed //继承多个基类{public:SleepSofa(){} voidFoldOut() //折叠与打开{cout<<”Folt/Outthesofa.\n”;}};voidmain(){SleepSofasf;sf.SitOn();sf.FoldOut();sf.Sleep();}Sitting...Folt/Outthesofa.Sleeping...运行结果classSleepSofa:publicSofa,p373.9派生类的对象和构造函数1.派生类的对象用某个基类声明一个派生类后,仍然可以利用基类来声明对象,而且所声明的对象和派生类所声明的对象不会冲突。voidmain(){sonob1; //用派生类son定义一个对象ob1parentob2; //用基类parent定义一个对象ob2cout<<”Displayderivedclassobjectob1”;ob1.show(); //对象ob1的显示结果ob1.change(); //调用从基类中继承的成员函数ob1.increase(); //调用派生类中声明的成员函数cout<<”Displaybaseclassobjectob2”;ob2.change();ob2.show();}Displayderivedclassobjectob1x=0Displaybaseclassobjectob2x=1运行结果3.9派生类的对象和构造函数1.派生类的对象void381.派生类的对象用某个基类声明一个派生类后,仍然可以利用基类来声明对象,而且所声明的对象和派生类所声明的对象不会冲突。voidmain(){sonob1; //用派生类son定义一个对象ob1parentob2; //用基类parent定义一个对象ob2cout<<”Displayderivedclassobjectob1”;ob1.show(); //对象ob1的显示结果ob1.change(); //调用从基类中继承的成员函数ob1.increase(); //调用派生类中声明的成员函数cout<<”Displaybaseclassobjectob2”;ob2.change();ob2.show();}Displayderivedclassobjectob1x=0Displaybaseclassobjectob2x=1运行结果1.派生类的对象voidmain()Displayder392.派生类对象的构造函数和析构函数#include<iostream.h>classbase{public:base(){cout<<”Constructingbase\n”;}~base(){cout<<”Destructingbase\n”;}};classderived:publicbase{public:derived(){cout<<”{cout<<”Constructingderived\n”;}~base(){cout<<”Destructingderived\n”;}};main(){derivedob;//donothingbutconstructanddestructobreturn0;}ConstructingbaseconstructingderivedDestructingderivedDestructingbase运行结果2.派生类对象的构造函数和析构函数Constructing40由此可得出一般性的结论:当创建派生类的对象时,如果基类包含构造函数,它将首先执行,派生类的构造函数紧随其后。当撤消派生类的对象时,它的析构函数将首先执行,如果基类包含析构函数,其析构函数将紧随其后。换句话说,构造函数按其引入的顺序执行,析构函数按其引入的相反顺序执行。在多层继承的情况下(即一个派生类成为另一个派生类的基类),一般规则是,构造函数按引入的顺序执行,析构函数按反序执行。由此可得出一般性的结论:当创建派生类的对象时,413.向基类构造函数传递参数
如何向基类的构造函数传递参数呢?答案是使用向一个或多个基类的构造函数传递参数的派生类构造函数声明的扩展格式即可。这种扩展的派生类构造函数声明的一般格式为:
derived_constructor(arg_list):base1(arg_list),base2(arg_list), ┇ baseN(arg_list);{//bodyofderivedconstructor}
其中,base1到baseN是派生类继承的基类名。注意,在有多个基类的情况下,用冒号将派生类的构造函数说明和基类分开,用逗号将基类分开。3.向基类构造函数传递参数42例3.9.2下面的例子使用了多个基类。
#include<iostream.h>classbase1{protected:inti;public:base1(intx){i=x;cout<<”Constructingbase1\n”;}//基类1的有参构造函数~base1(){cout<<”Destructingbase1\n”;}};classbase2{protected:intk;public:base2(intx){k=x;cout<<”Constructingbase2\n”;}//基类2的有参构造函数~base2(){cout<<”Destructingbase2\n”;}};例3.9.2下面的例子使用了多个基类。43classderived:publicbase1,publicbase2{intj;public:derived(intx,inty,intz):base1(y),base2(z)//derived使用x,y,z传递参数给基类构造函数{j=x;cout<<”Constructingderived\n”;}~derived(){cout<<”Destructingderived\n”;}voidshow(){cout<<i<<””<<j<<””<<k<<”\n”;}};main(){derivedob(3,4,5);//定义类derived的对象obob.show(); //显示435return0;}Constructingbase1Constructingbase2Constructingderived435DestructingderivedDestructingbase2Destructingbase1运行结果classderived:publicbase1,pub441.多态性(polymorphism)
多态性指的是一个接口名称具有多种功能,是面向对象程序设计的重要特性之一。C++有两种多态性,一是静态的多态性,一是运行时的多态性。所谓静态的多态性,是指操作作用的确切对象是在运行之前(即编译时)就能确定的,所以相应的反应也是在运行之前就能确定的,这种多态性在C++中是用重载实现的。所谓运行时的多态性,是指操作作用的确切对象依赖于运行的进程,其相应的反应必须在运行时动态给出。C++是通过虚拟函数(virtualfunction)实现这种多态性的。
2.静态联编与动态联编
联编是指将函数调用与相应的函数体代码彼此关联的过程,若此过程在程序开始运行前的编译阶段完成,称为静态联编。与静态联编不同,在程序运行时进行的联编方式,称为动态联编。一般说来,动态联编是通过指针来实现的。3.10多态性3.10多态性453.11重载1.函数重载(1)函数重载及其好处函数重载允许一个程序内声明多个名称相同的函数,这些函数可以完成不同的功能,并可以带有不同类型、不同数目的参数及返回值。使用函数重载可以减轻用户的记忆负担,并使程序结构简单、易懂。重载包含函数重载和操作符重载,是C++语言提供的一个重要特性。重载不仅为编译时的多态性提供了大部分支持,而且使得语言具有很大的灵活性和易扩展性。3.11重载1.函数重载重载包含函数重载46#include<iostream.h>intAbs(inti);doubleAbs(doubled);longAbs(longl);//程序包含三个名为Abs的重载版本的函数main(){cout<<Abs(-10)<<”\n”;cout<<Abs(-12.0)<<”\n”;cout<<Abs(-7L)<<”\n”;return0;}intAbs(inti){cout<<“usingintegerAbs()\n”;returni<0?-i:i;}doubleAbs(doubled){cout<<“usingdoubleAbs()\n”;returnd<0.0?-d:d;}longAbs(longl){cout<<“usinglongAbs()\n”;returnl<0?-l:l;}#include<iostream.h>47(2)函数重载的基本规则重载函数时我们必须遵守一个基本规则:你提供给编译程序的重载函数必须在它们的参数类型或参数数量上不同。
#include<iostream.h>intmyfunc(inti);intmyfunc(inti,intj); //重载函数具有不同数目的参数main(){cout<<myfunc(20)<<”\n”; //调用myfunc(inti)cout<<myfunc(3,4)<<”\n”; //调用myfunc(inti,intj)return0;}intmyfunc(inti){returni;}intmyfunc(inti,intj){returni*j;}(2)函数重载的基本规则48(3)重载成员函数在类的成员函数和构造函数上,也可以直接使用函数重载的概念。例如,假设你要写两个不同的函数来显示一个窗口,一个函数要求提供大小作为一个参数,另一个不要求参数但使用缺省尺寸。可以以这种形式写一对重载的成员函数:voidDisplayWindow();voidDisplayWindow(CRectwinRect);创建完这些重载的成员函数之后,就可以调用任意一个。这个语句将调用第一个DisplayWindow函数:
DisplayWindow();下面的语句将调用第二个DisplayWindow函数:CRECTwinRect(10,10,50,200);DisplayWindow(winRect);(3)重载成员函数49(4)重载构造函数在C++中,构造函数经常被重载。许多构造函数有两个甚至更多的版本。classExample{ intx;public: Example(){x=0;} //定义重载的构造函数 Example(inta){x=a;}voiddisplay();};┇定义重载的构造函数后,声明对象时就可以根据不同的参数来分别调用不同的构造函数。例如:voidmain(){ExampleA; //自动调用构造函数Example()ExampleB(3); //自动调用构造函数Example(inta)┇}#include<iostream.h>#include<stdio.h>classdate{intday,month,year;public:date(char*d);date(intm,intd,inty);voidshow_date();};date::date(char*d) //用字符串初始化{sscanf(d,”%d%*c%d%*c%d”,&month,&day,&year);}date::date(intm,intd,inty) //用整数初始化{day=d;month=m;year=y;}voiddate::show_date(){cout<<month<<”/”<<day;cout<<”/”<<year<<”\n”;}main(){dateob1(3,6,2000),ob2(”10/21/1999”);ob1.show_date();ob2.show_date();return0;}mm/dd/yyyymain(){chars[80];cout<<”Enternewdate:”;cin>>s;dated(s);d.show_date();return0;}(5)派生类中的重载函数#include<iostream.h>classAclass{private:intaVal;public:Aclass(){}~Aclass(){}intValue(intn){aVal=n+1;returnaVal;}};classBclass{private:floatbVal;public:intValue(floatn){bVal=n+2;returnbVal;}};intmain(){Bclassanob;cout<<anob.Value(100)<<’\n’;return0;}(6)作用域操作符intmain(){Bclassanobject;cout<<anobject.Aclass::Value(100)<<’\n’;//使用作用域操作符来调用Aclass::Value()return0;}2.操作符重载(1)编写操作符重载函数函数类型operator#(形参表){//函数体}(2)操作符重载函数的作用域Moneyoperator+(int){//函数体}(3)C++如何实现操作符重载(5)操作符重载举例AClass.operator++();AClassAClass::operator++(){//bodyoffunction}++aObject;#include<iostream.h>#include<math.h>#include<stdlib.h>#include<stdio.h>classMoney{private:doubledollars;doublecents;public:Money(){} //定义构造函数~Money(){} //定义析构函数 Money(doubled,doublec); //另一构造函数Money(double); //从double转换Moneyoperator++(); //操作符重载doubleGetAmount(double&n);};MoneyMoney::operator++(){cents++;if(cents>99){dollars++;cents=0;}return*this;}Money::Money(doubled,doublec){dollars=d;cents=c;}Money::Money(doublecash) //一个转换构造函数{ //将浮点值转换成Money值doublefrac,n; frac=modf(cash,&n); //一个math.h函数cents=frac*100;dollars=n;}doubleMoney::GetAmount(double&n){n=dollars;returncents;}intmain(){doublec,d;MoneymyCash(7.99);c=myCash.GetAmount(d);cout<<d<<"dollars\n";cout<<c<<"cents\n";++myCash;c=myCash.GetAmount(d);cout<<d<<"dollars\n";cout<<c<<"cents\n";return0;}7dollars99cents8dollars0centsMoneyoperator++();Moneyoperator++(intx);MoneyMoney::operator++(intx){cents++;if(cents>99){dollars++;cents=0;return*this;}}(4)重载构造函数50(4)重载构造函数在C++中,构造函数经常被重载。许多构造函数有两个甚至更多的版本。classExample{ intx;public: Example(){x=0;} //定义重载的构造函数 Example(inta){x=a;}voiddisplay();};┇
定义重载的构造函数后,声明对象时就可以根据不同的参数来分别调用不同的构造函数。voidmain(){ExampleA; //自动调用构造函数Example()ExampleB(3); //自动调用构造函数Example(inta)┇}(4)重载构造函数定义重载的构造函数后,声明51例:下面的程序创建了一个叫做date的类,它表示一个公历日期。
#include<iostream.h>#include<stdio.h>classdate{intday,month,year;public:date(char*d);date(intm,intd,inty);voidshow_date();};date::date(char*d) //用字符串初始化{sscanf(d,”%d%*c%d%*c%d”,&month,&day,&year);}例:下面的程序创建了一个叫做date的类,它表示一个公历日期52date::date(intm,intd,inty) //用整数初始化{day=d;month=m;year=y;}voiddate::show_date(){cout<<month<<”/”<<day;cout<<”/”<<year<<”\n”;}main(){dateob1(3,6,2000),ob2(”10/21/1999”);ob1.show_date();ob2.show_date();return0;}date::date(intm,intd,inty53(5)派生类中的重载函数在类继承中,也可以出现基类的成员函数和派生类的成员函数名字相同的情形。这时,在派生类中说明的任何重载超越在基类中说明的函数。
#include<iostream.h>classAclass{private:intaVal;public:Aclass(){}~Aclass(){}intValue(intn){aVal=n+1;returnaVal;}};classBclass{private:floatbVal;(5)派生类中的重载函数54public:intValue(floatn){bVal=n+2;returnbVal;}};intmain(){Bclassanob;cout<<anob.Value(100)<<’\n’;return0;}
上例中,名为Bclass的类从Aclass类中派生出来。Aclass的定义中包含了一个名为Value的函数。
public:上例中,名为Bclass的类从55(6)作用域操作符
如果由派生类说明的成员函数超越了由基类说明的成员函数,那么可以用作用域操作符(::)的指引来访问在基类中说明的成员函数。
例:可以用下面的main()函数来代替上例中的main()函数。
intmain(){Bclassanobject;cout<<anobject.Aclass::Value(100)<<’\n’;//使用作用域操作符来调用Aclass::Value()return0;}101运行结果(6)作用域操作符101运行结果562.操作符重载操作符重载和函数重载功能一致。通过操作符重载,可以重新定义C++语言中的已有操作符的功能,使得它们能针对不同的情况执行不同的操作。操作符重载使得程序员可把C++操作符的定义扩展到操作数是对象的情况。
(1)编写操作符重载函数函数类型operator#(形参表){//函数体}
其中,operator是关键字(所以有时也称操作符重载函数为operator函数),#表示欲定义的操作符,函数类型指明返回值类型,通常与类类型一致或为void类型。2.操作符重载57(2)操作符重载函数的作用域
当重载一个操作符时,普通的作用域规则也适用,操作符被重载的作用范围就是重载它的函数出现的作用范围。
Moneyoperator+(int){//函数体}(2)操作符重载函数的作用域58(3)C++如何实现操作符重载在C++语言中,操作符只能对属于固有类型的常量和变量进行操作。而且,C语言中操作符的含义是固定的。在C++中你可以根据自己的意图定义操作符。当你重定义一个操作符时,你可以用代码记号来表达对用户自定义类的操作。当C++编译程序对一个表达式进行求值时,它会检查一系列的与操作符号混在一起的值(包括常量和变量)。当遇到一个操作符时,它首先判断此操作符是用作一元或者是二元操作符。然后,编译程序根据优先规则和组织规则对此表达式求值,同时根据这些规则要求给操作符和操作数分组。编译程序判定它遇到的操作符的操作数据的类型。只有在那时,它才知道此表达式是否合法,而且如果合法,则应当产生什么代码。(3)C++如何实现操作符重载59例:重载一元操作符++,用来递增Money类的值。例中Money类用来存放钱数,它包含两个双精度的成员变量。一个成员变量用于存储结果的元值,另一个用于存储结果的分值。#include<iostream.h>#include<math.h>#include<stdlib.h>#include<stdio.h>classMoney{private: doubledollars; doublecents;public: Money(){} //定义构造函数 ~Money(){} //定义析构函数 Money(doubled,doublec); //另一构造函数Money(double); //从double转换 Moneyoperator++(); //操作符重载 doubleGetAmount(double&n);};例:重载一元操作符++,用来递增Money类的值。例中Mon60MoneyMoney::operator++(){ cents++; if(cents>99) { dollars++; cents=0; } return*this;}Money::Money(doubled,doublec){ dollars=d;cents=c;}Money::Money(doublecash) //一个转换构造函数{ //将浮点值转换成Money值 doublefrac,n; frac=modf(cash,&n); //一个math.h函数cents=frac*100;dollars=n;}MoneyMoney::operator++()61doubleMoney::GetAmount(double&n){ n=dollars; returncents;}intmain(){ doublec,d; MoneymyCash(7.99);
c=myCash.GetAmount(d); cout<<d<<"dollars\n"; cout<<c<<"cents\n";
++myCash;c=myCash.GetAmount(d); cout<<d<<"dollars\n";cout<<c<<"cents\n";return0;}7dollars99cents8dollars0cents
运行结果doubleMoney::GetAmount(double62例:重载加法操作符,使其将两个Money类的值相加并将结果存在一个Money对象中
。#include<iostream.h>#include<math.h>#include<stdlib.h>classMoney{private: doubledollars; doublecents;public: Money(){} //定义构造函数 ~Money(){} //定义析构函数 Money(doubled,doublec); //另一构造函数Money(double); //转换构造函数 Moneyoperator+(Moneym); //操作符重载doubleGetAmount(double&n);};例:重载加法操作符,使其将两个Money类的值相加并将结果存63MoneyMoney::operator+(Moneym){ cents+=m.cents; dollars+=m.dollars; if(cents>99) { dollars=dollars+1; cents=cents-100; } return*this;}Money::Money(doubled,doublec){ dollars=d;cents=c;}Money::Money(doublecash) //一个转换构造函数{ //将浮点值转换成Money值 doublefrac,n; frac=modf(cash,&n); //一个math.h函数cents=frac*100;dollars=n;}MoneyMoney::operator+(Moneym64doubleMoney::GetAmount(double&n){ n=dollars; returncents;}intmain(){ doublec,d; MoneymyCash(3.49); MoneyyourCash(50.86); myCash=myCash+yourCash; c=myCash.GetAmount(d); cout<<"Younowhave"<<d<<"dollars.\n"; cout<<"Youalsohave"<<c<<"cents.\n"; return0;}Younowhave54dollars.Youalsohave35cents.
运行结果doubleMoney::GetAmount(double651.多态性与虚拟函数
C++的多态性是通过虚拟函数来实现的,而虚拟函数是基于继承的环境中。在继承关系下,派生类作为基类的子类,在任何要求基类对象的地方使用派生类对象是有意义的。我们可以用基类指针通过间接或直接的方式指向它的任何派生类的对象。当通过一个基类指针引用一个派生类对象时,只能调用基类所定义的成员函数,而无法调用派生类定义的成员函数,即使派生类重新定义了基类的成员函数,所调用的仍是基类中的而不是派生类中的成员函数。当通过指针引用一个对象时,指针的类型决定了可以调用的成员函数。
3.12虚拟函数3.12虚拟函数66#include<iostream.h>classPerson{public: voidDisplay(){cout<<“classPerson\n”;}};classChild:publicPerson{public:voidDisplay(){cout<<”classChild\n”;}};classOld:publicPerson{public:voidDisplay(){cout<<”classold\n”;}};voidmain(){Personpeople1;Person*p;Childpeople2;Oldpeople3;p=&people1;p->Display();p=&people2;p->Display();p=&people3;p->Display();}
classPersonclassPersonclassPerson运行结果#include<iostream.h>public:cl67
修改上面的例子,将基类的成员函数Display()声明为虚拟函数。#include<iostream.h>classPerson{public: virtualvoidDisplay() //声明基类的成员函数为虚拟函数{cout<<“classPerson\n”;}}; //定义基类PersonclassChild:publicPerson{public:voidDisplay(){cout<<”classChild\n”;}}; //定义派生类Child修改上面的例子,将基类的成员函数Display(68classOld:publicPerson{public:voidDisplay(){cout<<”classold\n”;}}; //定义派生类Oldvoidmain(){Personpeople1;Person*p; //定义基类指针pChildpeople2;Oldpeople3;p=&people1; //基类指针指向基类对象people1p->Display(); //调用基类Person的成员函数Display()p=&people2; //基类指针指向基类对象people2p->Display(); //调用派生类Child的成员函数Display()p=&people3; //基类指针指向基类对象people3p->Display(); //调用派生类Old的成员函数Display()}cl
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 临时密码分享功能门锁创新创业项目商业计划书
- 南开大学滨海学院《综合日语》2023-2024学年第一学期期末试卷
- 陕西科技大学《乒乓球》2023-2024学年第一学期期末试卷
- 北京市丰台区第十二中学2024年八年级物理第一学期期末学业质量监测试题含解析
- 长沙医学院《药剂学实验》2023-2024学年第一学期期末试卷
- 2025版彩票销售管理系统设备租赁合同
- 2025版跨境电子商务平台服务采购合同规范样本
- 2025版保姆家政服务标准化合同
- 二零二五年度建筑施工合同标准文本汇编
- 2025版生态旅游社区物业管理服务合同
- 广元城市IP打造营销规划方案
- 2025年项目管理专业资格考试试题及答案
- 房屋租用合同4篇
- 非公企业党建培训课件
- 2025区域型变电站智能巡视系统技术规范
- (2025)社区网格员笔试考试题库及答案
- 汛期公交安全课件
- 环境影响管理方案(3篇)
- TJA围手术期血液管理课件
- DB4401-T 5-2018房屋面积测算规范-(高清现行)
- 新教材波音手册查询(高级)课件
评论
0/150
提交评论