![C++面向对象程序设计课件_第1页](http://file4.renrendoc.com/view/4ded1a912c0b96f34d0ac6cbbfdde2c8/4ded1a912c0b96f34d0ac6cbbfdde2c81.gif)
![C++面向对象程序设计课件_第2页](http://file4.renrendoc.com/view/4ded1a912c0b96f34d0ac6cbbfdde2c8/4ded1a912c0b96f34d0ac6cbbfdde2c82.gif)
![C++面向对象程序设计课件_第3页](http://file4.renrendoc.com/view/4ded1a912c0b96f34d0ac6cbbfdde2c8/4ded1a912c0b96f34d0ac6cbbfdde2c83.gif)
![C++面向对象程序设计课件_第4页](http://file4.renrendoc.com/view/4ded1a912c0b96f34d0ac6cbbfdde2c8/4ded1a912c0b96f34d0ac6cbbfdde2c84.gif)
![C++面向对象程序设计课件_第5页](http://file4.renrendoc.com/view/4ded1a912c0b96f34d0ac6cbbfdde2c8/4ded1a912c0b96f34d0ac6cbbfdde2c85.gif)
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
2023/11/161教师:何国斌电话箱:hgb07123@163.comC++:面向对象程序设计2023/11/162类的诞生类(Class)是面向对象思想中的一个重要组成部分。如何在计算机中将“类”表达出来?在C语言中,当定义结构体(struct):structSAMPLE{ intmember1,member2;}var1;以后,就可以使用结构中的成员:intvar=var1.member1*var1.member2;2023/11/163将结构体成员的使用代码写成一个函数,得到:intcaluc(structSAMPLEvar){returnvar.member1*var.member2;}操作结构变量类的诞生2023/11/164然后,将上面的函数放入结构体中structSAMPLE{
intcalcu() { returnmember1*member2; } intmember1,member2;};数据成员函数成员类的诞生2023/11/165这样的结构体就具有了一定的属性(member1和member2),也具有一定的行为(函数calcu),它就是“类”的雏形。该结构体的使用方法如下:structSAMPLEvar;var.member1=var.member2=10;inttemp=var.calcu();类的诞生2023/11/1662.1类2023/11/1672.1.1类的定义classCCompanyStaff{//BEGIN
//声明成员函数voidSetBasicSal(floatsal);//设置基本工资
//声明(定义)数据成员intm_iStaffNum;//工作编号charm_cName[20];//姓名floatm_fRateOfAttend;//出勤率floatm_fBasicSal;//基本工资};//END多了一个函数(SetBasicSal),其它都象结构体。struct
class2023/11/168CCompanyStaff类的定义以关键字class开始。其后是类名。“{}”表示类定义的开始和结束,最终以分号结束。一般在类中只声明成员函数的原型,而函数的实现(即函数体的定义)则放在类外完成。“函数原型”,即只声明函数名、参数类型和返回值类型,而不包括函数体代码。2.1.1类的定义2023/11/169函数的定义则可在函数(SetBasicSal)声明之后:voidCCompanyStaff::SetBasicSal(floatsal){m_fBasicSal=sal;}函数定义:函数名前多了一个类作用域运算符(CCompanyStaff::),用于标识该函数定义属于哪一个类。2.1.1类的定义2023/11/1610对于C++语言的编程习惯:1、将类的声明存放于“*.h”或“*.hpp”的头文件中,每个类一个声明文件。2、将类的定义存放于“*.cpp”文件中,与相应的声明文件一一对应.c
.cpp2.1.1类的定义2023/11/1611封装成类的好处——可以实现数据隐藏。封装也确定了类成员的访问属性。对象的封装性2023/11/1612对象的封装性C++语言引入面向对象思想,引入类(对象)的概念之后,必然会具有一些新的特性。面向对象思想中类(对象)的基本特性之一:封装性。封装性:直观理解,类将属于它的数据(成员变量)和针对数据的操作(成员函数)包裹在一起就是一种封装。封装的目的:保护类(对象)的实现。对封装属性进行细分:公有的;私有的;保护的。2023/11/1613C++语言为了实现面向对象的封装,引入了三个新的“修饰”关键字:public
(公有的):公开的,可见的。对象成员(变量与方法)可以在对象外使用。private
(私有的):不可见的。成员只能在对象内部使用。protected
(保护的):受到保护的。成员也只能在内部使用。(以后再讨论)C++类的成员(变量和方法)具有了各自不同的属性。对象的封装性2023/11/1614classCCompanyStaff{public:voidSetBasicSal(floatsal);//设置基本工资private:intm_iStaffNum;//工作编号charm_cName[20];//姓名floatm_fRateOfAttend;//出勤率floatm_fBasicSal;//基本工资};//END对象的封装性2023/11/1615CCompanyStaffstaff;staff.SetBasicSal(600);//合法的,因为SetBasicSal是一个公开(public)的方法。staff.m_iStaffNum=100;//非法的,因为m_iStaffNum是一个私有(private)的变量。voidCCompanyStaff::SetBasicSal(floatsal){m_fBasicSal=sal;//SetBasicSal是对象的成员,所以它的定义中可以访问m_fBasicSal私有变量。}对象的封装性2023/11/16161、C++类定义中的缺省属性为私有的(private)。2、每个修饰符的作用范:从一个修饰符开始,直到另外一个修饰符时结束(或类结束了)。3、C++类中的成员(变量和方法)都应该明确地指明它们各自的属性。对象的封装性2023/11/16172.1.2类成员的访问权限封装所实现的数据隐藏是面向对象程序设计的一个关键特性——隐藏一个类的数据从而使其他类无法访问。隐藏由封装实现,所以隐藏所实现的类成员的访问控制权限也和封装方法一一对应。对应于封装,类成员有3种访问权限:公有类型(public)、私有类型(private)保护类型(protected)。2023/11/1618类
私有
公有数据或函数数据或函数无法从类外访问允许从类外访问2.1.2类成员的访问权限2023/11/1619公有类型的成员定义了类的接口,由关键字public声明,在类外只能访问公有成员。私有类型的访问权限为私有的成员由关键字private声明,它们只能被类本身的成员函数访问,来自类外部的任何访问都是非法的。保护类型的成员与私有成员类似,区别仅在于继承过程中,保护类型的成员可以被所在类的派生类成员函数访问;而这一点对于私有成员来说是非法的。2.1.2类成员的访问权限2023/11/1620类的成员函数可以访问类的所有成员,没有任何限制;而类对象访问类的成员就要受到访问控制符的限制。访问权限举例:sclass2_1_demo.hsmain2_1.cpp类对象和类的成员函数对数据成员的访问权限不同。2.1.2类成员的访问权限2023/11/1621外部接口是类外访问类中私有数据的桥梁。声明时,类中不同访问权限的成员可以按任意顺序出现。但依然涉及到一个编程习惯问题:“以数据为中心”
“以行为为中心”
国际公约。2023/11/1622建议把一个类的数据成员都声明为私有的访问权限。这样做有两个好处:一是信息隐藏,即实现封装,把类的内部实现和外部表现分开,让使用者无需了解类的实现细节;二是数据保护,将类的重要信息保护起来,以免被其他程序不恰当地修改。2.1.2类成员的访问权限2023/11/16232.1.3成员函数的实现被隐藏的数据可以有成员函数来访问,对数据的操作也体现在成员函数中:成员函数决定对象的操作行为。它是程序算法的实现部分。它也是对封装的数据进行操作的唯一途径。它有两种方式:类外实现和类内实现。注:数据对成员函数无法隐藏。2023/11/16241.一般实现方式类外实现成员函数的形式如下:返回值类型类名::成员函数名(形式参数表){
函数体}例2.2企业员工类的实现。sclass2_2_companyStaff.hs2_2\sclass2_2_companyStaff.cpps2_2\smain2_2.cpp2023/11/16252.内联函数方式内联函数是指程序在编译时用函数的代码替换每一处函数调用的地方。内联的优点——以空间换时间。内联的两种方式:系统默认:代码在类声明中
例:sclass2_2_0_companyStaff.h函数前加关键字inline:inline<函数返回值类型><函数名>(<形参表>){<函数体语句>}
参见例:smain2_3.cpp。2023/11/1626内联和宏替换宏替换是在编译前由预处理程序进行预处理,它只做简单的字符替换而不涉及语法检查。而内联函数是在编译时处理的,编译程序能识别内联函数,对它进行语法检查。2023/11/16272.2类与对象类(class):具有相同或相近的性质和行为的事物集合。对象(object):theinstanceofaclass。一个类的实例,即类的性质(变量)具体化之后成为对象。一个对象是类的一种特殊情况。一个类会有很多的对象,一个对象属于一个类。它们是一对多的关系。2023/11/1628C++中的对象(一)当C++中类的成员变量被赋与特定的值之后,它即成为一个C++的对象。CCompanyStaffstaff;//一个对象变量staff.m_iStafNum=12345;staff.m_cName=“张三”
;staff成为类CCompanyStaff的一个对象,它有了一个工作编号,对应一个人名。当然,还会有很多其它的对象,比如staff1、staff2等等2023/11/1629C++中的对象(二)1、对象针对计算机而言,就是代码中的一个变量。2、C++中对象变量的使用与C语言中结构体变量的使用方法一样。CCompanyStaffstaff1,*pstaff1,staff3[10];staff1.m_iStafNum=11111;pstaffm_iStafNum=22222;staff[0].m_iStafNum=33333;2023/11/1630C++类的对象化(一)C++中对象是类的变量,但它不象变量那么单纯。C++提供一套特殊的机制实现类到对象的转换。classCCompanyStaff{public:
CCompanyStaff();~CCompanyStaff();voidSetBasicSal(floatsal);//设置基本工资private:intm_iStaffNum;//工作编号};//END2023/11/1631对象的创建和销毁创建对象时,“对象存放在何处??”需要向操作系统申请一定的内存空间用于存放新建的对象。为对象分配存储空间主要有:静态分配和动态分配两种方式。2023/11/1632静态分配:可以将对象或静态成员存放在栈中或静态存储区域中。动态分配:
动态内存分配是指在堆(也称自由内存)中分配存储单元,即为对象动态从堆中分配内存。使用操作符new分配内存空间;使用操作符delete释放内存空间。对象的创建和销毁2023/11/16332.1.1构造函数与析构函数C++语言为了保证一个对象被初始化(类的实例化),定义了一组特殊的方法(函数),专门用于对象生成时的初始化。构造函数(constructor)——与类名称相同,没有返回值。它在对象生成之时自动执行。析构函数(destructor)——在类名前加~作为函数名的函数,没有返回值,也没有参数。它在对象结束时自动执行。2023/11/1634C++要求类设置一个专门的成员函数来负责类中所有对象的初始化,这个成员函数就是构造函数。构造函数的作用就是在对象被创建时利用特定的值构造对象,将对象初始化到一个特定的状态。声明一个构造函数的语示格式如下:public:类名(<参数表>);2.1.1构造函数与析构函数2023/11/1635构造函数可以由程序设计人员自己编写。也可以由系统提供。例2.4构造函数举例。sclass2_4_companyStaff.hsmain2_4.cpp2.1.1构造函数与析构函数2023/11/1636重载构造函数所谓重载构造函数,是指同一个构造函数名,具有不同的实现。例2.5在员工管理系统中,创建一个对象时,还可以一次性地给对象的姓名、出勤率、基本工资等几个数据成员都赋初值,这就需要重载构造函数。sclass2_5_companyStaff.hsclass2_5_companyStaff.cppsmain2_5.cpp2023/11/1637需要注意一点,当构造函数带默认参数时,要谨防出现歧义。例2.6下面这个程序存在歧义。sclass2_6.hsmain2_6.cpp当创建对象d2时,有二义性。2.1.1构造函数与析构函数2023/11/1638拷贝构造函数拷贝构造函数是用来复制对象的一种特殊的构造函数。声明拷贝构造函数的语法格式如下:class类名{public:
类名(类名&对象名);};只有一个参数2023/11/1639例2.7通过水平坐标和垂直坐标来确定屏幕上的一个点。sclass2_7_point.hsmain2_7.cppCPointb(a);CPointc=a;拷贝构造函数2023/11/1640析构函数析构函数与构造函数的作用几乎正好相反,当一个对象消失时,或用delete删除用new创建的对象时,系统都会自动调用类的析构函数,做一些清理工作。声明一个析构函数的语法格式如下:classDemo{public: Demo(<参数表>); ~Demo(void);}2023/11/1641析构函数不能重载。
//析构函数
~CCompanyStaff(void) { cout<<"对象"<<m_cName<<"消亡"<<endl; }析构函数2023/11/1642当某对象消亡时,系统会自动调用该对象的析构函数。而且调用的顺序是:最后创建的对象最先消亡,即最先调用其析构函数;相反地,最先创建的对象最后消亡,即最后调用其析构函数。 如果不显式地定义析构函数,系统也会生成一个默认的析构函数,它是一个空的析构函数,不做任何事情。析构函数2023/11/1643C++类的对象化(附)classCCompanyStaff{public:
CCompanyStaff();CCompanyStaff(intnum,char*pName);~CCompanyStaff();voidSetBasicSal(floatsal);//设置基本工资……………};//ENDCCompanyStaffstaff(12345,“张三”);2023/11/1644C++类的对象化(附)1、构造函数与析构函数被“隐含”调用,即不管愿意与否,它们都会被“强制”地执行。2、C++类都有缺省的构造函数,即没有参数的构造函数。它也是“强制”的,没有定义每个类也有一个这样的构造函数。3、编程习惯:不管有用没用,希望编程人员为每个类定义明确的构造与析构函数2023/11/16452.2.3对象成员的访问可以通过对象名,也可以通过对象地址来访问一个对象:<类名><对象名>; <对象名>.<成员名> //访问公有数据成员<对象名>.<成员名>(<参数表>)
//访问公有成员函数CCompanyStaffstaff1("LiHua"); staff1.SetBasicSal(4000.0);2023/11/1646<类名>*<对象指针名>;<对象指针>-><成员名> //访问公有数据成员<对象指针>-><成员名>(<参数表>)
//访问公有成员函数CCompanyStaff*pstaff;pstaff=newCCompanyStaff("LiuMei",0.95,3000.0);pstaff->GetName()
2.2.3对象成员的访问2023/11/1647例2.8在员工管理系统中,建立两个对象分别用两种方式去访问类成员。sclass2_8_companyStaff.hsclass2_8_companyStaff.cppsmain2_8.cpp2.2.3对象成员的访问2023/11/1648对象指针:普通对象指针例2.9普通对象指针举例。sclass2_9_objPointer.hsmain2_9.cpp
CPointerExam*pointer; pointer=&obj;
pointer->SetNum(2);对象指针在使用之前一定要初始化,为其动态分配存储空间;使用完毕必须释放该对象指针所代表的资源。2023/11/1649对象的this指针每个对象都拥有自己独立的数据成员。而类中的所有对象使用相同的成员函数,成员函数在内存中只有一份。每个对象隐含了一个常量指针,称为this指针,用于指向当前发送消息的对象,以识别当前调用成员函数的对象究竟是谁。当通过一个对象调用成员函数时,系统先将该对象的地址赋给this指针,成员函数在对对象的数据进行操作时,就隐含地使用了this指针。2023/11/1650CCompanyStaff(charcName[]) {
strcpy(m_cName,cName);}CCompanyStaff(charcName[],CCompanyStaff*constthis) {
strcpy(this->m_cName,cName);
}显示指明this对象的this指针2023/11/1651CCompanyStaff(charcName[]) {
strcpy(this->m_cName,cName);
}隐含this对象的this指针2023/11/1652一般不需要显示指出this,只有当函数需要返回当前对象自身的时候,才显式地使用它。例2.10this指针举例。sclass2_10_this.hsmain2_10.cpp
CSampleAdd(CSamples1,Csamples2) { this->n=s1.n+s2.n;
return(*this); }对象的this指针2023/11/1653类成员指针(1)类数据成员指针如果指针指向类数据成员的地址,则这个指针称为类数据成员指针;<类型><类名>::*<指针名>
sclass2_11_dataPointer.hsmain2_11.cpp
intCSample::*p=&CSample::m; CSamples1; s1.*p=20;类数据成员指针只能指向公有数据成员。2023/11/1654(2)类成员函数指针如果指针指向类成员函数的地址,则称为类成员函数指针。声明一个类成员函数指针并为其赋值的语法格式如下:<类型>(<类名>::*<指针名>)(<参数表>)
<指针名>=<类名>::<成员函数名>;
使用指向成员函数的指针调用函数的格式如下:(*<指针名>)(<实参表>)
类成员指针2023/11/1655//主文件:smain2_12.cpp#include"sclass2_8_companyStaff.h"
//包含sclass2_8_companyStaff.h的代码#include<iostream>usingnamespacestd;voidmain(void){ void(CCompanyStaff::*pFunc)(float);
//声明一个类成员函数指针
CCompanyStaffstaff("LiHua");
//创建一个对象staff pFunc=CCompanyStaff::SetBasicSal;//指针初始化指向SetBasicSal函数
(staff.*pFunc)(3000);//相当于staff调用SetBasicSal() cout<<"员工LiHua的基本工资是"<<staff.GetBasicSal();}类成员指针2023/11/1656对象数组例2.13对象数组应用举例sclass2_13_fruit.hsmain2_13.cpp
CFruitd[4];
//创建一个对象数组,相应调用4次构造函数如需要建立一个对象数组,必须满足以下条件:至少有一个构造函数没有参数或只带默认参数。2023/11/1657Static静态成员static(静态)修改类中的成员(变量和函数),表明该成员只有一个副本,与具体的对象没有关系。static后的成员变量可以用于对象间的数据共享。static后的成员函数只能使用static成员变量。2023/11/1658static成员的使用当一个成员被static修饰之后,它的使用不再属于各个对象,而是属于这个类。classCCompanyStaff{public:staticvoidSetBasicSal(floatsal);
intm_iStaffNum;};//ENDCCompany::m_iStaffNum=1234;CCompany::SetBasicSal(30000);2023/11/1659静态成员类的静态成员拥有一块单独的存储区。该类的所有对象都共享这块静态存储空间这就为对象提供了一个相互通信的方法。静态成员由关键字static标识。它属于类而不属于对象。它分为静态数据成员和静态成员函数。2023/11/1660声明一个静态数据成员的语法格式如下:static<数据类型><静态数据成员名>静态数据成员在使用前也要初始化,但它的初始化不能在构造函数中进行,在类外进行。<数据类型><类名>::<静态数据成员名>=〈初始值〉;其访问语法格式如下:<类名>::<静态数据成员名>静态成员2023/11/1661sclass2_14_companyStaff.hsclass2_14_companyStaff.cppsmain2_14.cpp
staticints_iCount;intCCompanyStaff::s_iCount=1000;静态成员2023/11/1662静态成员函数是一种特殊的成员函数,它属于整个类,也为同类中所有对象共同拥有。只要类存在,静态成员函数就可以使用。可以通过类名和对象名来调用。其定义语法格式如下:<类名>::<静态成员函数名>(<参数表>)例2.15静态成员函数举例。sclass2_15.hsmain2_15.cpp静态成员2023/11/1663静态成员函数一般不访问普通数据成员,它的作用主要是访问和操作同类中的静态数据成员。类的普通成员函数都拥有this指针。而静态成员函数没有this指针,但可以通过类名或对象名来实现对它的访问。静态成员2023/11/1664对象封装性的局限已经学习过类对于成员的封装(public,private,protected)封装性有效地保护了对象的内部细节,使得对象的使用和对象的实现分开,互相不产生影响。同时,封装性带来负面影响:1、C++为实现对象的封装,必然会做一些额外的工作,从而导致程序的效率下降。2、一个对象封装的太好,也会让该对象很难使用,也很难实现。2023/11/1665突破对象封装C++提供了友元(friend)来解决由封装性带来的问题。friend关键字修改函数或类,因此对于一个类而言,它有友元函数或友元类。friend用于应对编程中一些比较特殊的情况(如提高效率),绝大多数情况下不需要使用。乱使用只会使C++变成C,甚至更糟。2023/11/16665.友元关系一个类可以声明一个友元关系,一起来共享类中的所有成员。友元如果是一个函数,则称为友元函数;如果是一个类,则称为友元类。友元函数是在类中由关键字friend修饰的非成员函数。友元函数可以是一个普通的函数,也可以是其他类的成员函数。虽然它不是本类的成员函数,但是在它的函数体中可以通过对象名访问类的私有和保护成员。2023/11/1667例2.16友元函数与成员函数的比较。sclass2_16.hsmain2_16.cpp(例中的友元关系会发生错误)friendvoidFriendFunc(CSample*cp,inta)//增加一个对象指针参数{ cp->i=a;//对象指针参数为i指明当前所属对象}需要操作对象5.友元关系2023/11/1668友元函数的特点:第一、友元函数可以直接访问该类的所有成员,但它不是该类的成员函数,可以像普通函数一样在任何地方调用。第二、友元函数不属于任何类,因此可以放在类说明的任何位置,既可以在public区,也可以在private区。第三、友元函数不需要通过对象或对象指针来调用,可以直接调用即可。5.友元关系2023/11/1669友元类在类中可把另一个类声明为友元类,如类B是类A的友元类,则类B中的所有成员函数都是类A的友元函数,都可以访问类A的私有和保护成员。友元类的声明语句如下:classB;//前向引用声明classA{ …… friendclassB;//B为A的友元类
……}5.友元关系2023/11/1670例2.17编写一个有关栈结构的程序,要求实现入栈和出栈。其中有两个类,一个是结点类CNode,拥有结点值和指向下一结点的指针;另一个是栈类CStack,它拥有栈的头指针。由此生成的链式结构如图2.3所示。321头指针栈顶栈底指向下一结点的指针5.友元关系2023/11/1671sclass2_17_stack.hsclass2_17_stack.cppsmain2_17由于栈类CStack是结点类CNode的友元类,所以栈CStack类的所有成员函数都成为类CNode的友元函数。因此Push()和Pop()可以访问结点类CNode对象的私有成员并对其进行操作。封装是对象与外界之间一堵不透明的墙,而友元恰好在这堵墙上开了一个小孔,它以牺牲信息隐藏、削弱封装性为代价来实现数据共享。5.友元关系2023/11/16722.3C++输入输出流对象标准输入输出流:数据从程序中流入到屏幕或磁盘文件,即输出流;数据从键盘流入到程序中,即输入流。所谓流,是从源到矢的数据流的抽象引用,具体地说,就是数据从一个对象流向另一个对象。在进行I/O操作时,首先执行打开操作,使流和文件发生联系,建立联系后的文件才允许数据流入或流出,输入输出结束后,执行关闭操作使文件与流断开联系。2023/11/16732.2.1标准输入输出流iosistreamostreamifstreamiostreamofstreamstreambuffilebufstringbuffstreambaseistream_withassignostream_withassigniostream_withassignfstreamC++流类库C++提供了一个流类库,它具有两个平行的根基类:streambuf类和ios类。2023/11/1674streambuf类主要负责缓冲区的处理,提供对缓冲区的低级操作。ios类是流基类,它及其派生类提供用户使用流类所需的接口,支持对streambuf的缓冲。由它可以派生出输入流类istream和输出流类ostream等。两个根基类以及由它们派生出的所有流类(当然包括istream类和ostream类)都被定义在名为iostream的头文件中。因此须用“#include”编译指令将iostream头文件包含进来。C++流类库2023/11/16752023/11/1676系统预定义的一些流:进行输入输出常用的标准输入流对象cin和标准输出流对象cout,还有未被缓冲的标准错误输出cerr和被缓冲的标准错误输出clog。2.预定义输入/输出流cin和cout2023/11/1677cin是通用输入流类istream_withassign的对象,与标准输入设备连接。它通过重载运算符“>>”执行输入操作,在流操作中将“>>”称为提取运算符。cin从输入流中取出数据,数据从提取运算符“>>”处流进程序。2.预定义输入/输出流cin和cout2023/11/1678cout是通用输出流类ostream的对象,与标准输出设备连接。通过重载运算符“<<”执行输出操作,在流操作中,将“<<”称为插入运算符。插入运算符“<<”向输出流发送字符。实际上,位于插入运算符右侧的字符串被存储在“<<”左侧的流中。cout<<m<<""<<n<<endl;cout<<m;cout<<“
“;cout<<n;cout<<endl;2.预定义输入/输出流cin和cout2023/11/1679预定义操纵符操纵符是直接插入到流中的格式化指令,一般都定义在ios_base类中和<iomanip>头文件中,分为带参数和不带参数两类。2023/11/16802.预定义输入/输出流cin和cout例如:cout<<setw(5)<<n<<endl;表示设置输出n时所占的字节长度为5,输出n后换行。2023/11/16812.3.2文件输入输出流输入是指从磁盘文件流向内存。输出是指从内存流向磁盘。
C++提供了3个文件流类:ofstream,ifstream,fstream,都定义在头文件fstream.h中。其中fstream类以ofstream类和ifstream类为基类。ofstream类:输出流类,用于向文件中写入内容;ifstream类:输入流类,用于从文件中读出内容;fstream类:输入输出流类,用于既要读又要写的文件操作。2023/11/1682ofstream::ofstream(char*pFileName,intmode=ios::out,intprof=filebuf::openprot);第一个参数用于指业文件路径及文件名字符串,第二个参数说明文件打开方式,第三个参数说明文件保护方式。例2.18给出程序的执行结果smain2_18.cpp文件流对象构造函数2.3.2文件输入输出流2023/11/16832.3.2文件输入输出流2023/11/16842.3.2文件输入输出流2023/11/1685(1)输入流成员函数
open()close()get()getline()read()(2)输出流成员函数put()write()2.常用输入输出流成员函数2023/11/16862.常用输入输出流成员函数例2.19下面的程序将用户输入显示到屏幕上,输入字母y时输出OK并结束。smain2_19.cpp
例2.20连续读入一串字符,直到遇到字符q时停止,然后输出这个字符串。要求字符个数最多不超过79个。smain2_20.cpp
例2.21将英文字母及对应的ASCⅡ值输出到屏幕上。smain2_21.cpp
2023/11/1687C++流的应用1、C++专门为交互式输入与输出定义了两个对象:cin用于键盘的输入;cout用于显示屏的输出。2、所有的C++流都有两个操作符<<
和>><<:输出(插入)操作符。流对象<<数据;>>:输入(提取)操作符。流对象>>数据;2023/11/1688输入输出流应用举例例2.22员工管理系统中的输入输出sclass2_22_companyStaff.hsclass2_22_companyStaff.cppsmain2_22.cpp2023/11/1689习题课后习题、作业习题1习题2习题42023/11/1690Thanks!TheEnd2023/11/1691派生类的构造函数与析构函数第三章继承132继承方式类的继承与派生4虚基类5聚合类6继承应用实例本章内容2023/11/1692
3.1类的继承与派生3.1.1继承与派生实例继承是软件重用的一种形式。继承是将自然界中存在的普遍和特殊关系用程序设计的方式进行分类描述;在设计新类时,允许重用某个原有类的所有特征,并在此基础上添加新类的新特征。被重用的原有类称为基类baseclass而新创建的类称为派生类derivedclass。派生类不会影响到原有类的结构。2023/11/1693实例:某一小型公司的实例企业员工工作编号姓名……显示企业员工信息计算实际发放工资……行政人员(与企业员工类相同)计算实际发放工资……经理公司总销售额提成比例(其余同企业员工类)计算实际发放工资……销售人员个人销售额提成比例(其余同企业员工类)计算实际发放工资……2023/11/16943.1类的继承与派生针对该公司的情况,先设计:一个一般员工类CCompanyStaff,代表员工的共性;让三个新类分别继承一般员工类CCompanyStaff,然后根据各自的差异性,新类可以对继承来的内容进行改造。2023/11/16953.1.2派生类的定义从已有类产生新类的过程就是类的派生。定义语法如下:
class<派生类名><继承方式>:<基类名1>, <继承方式>:<基类名2>…{ <派生类成员的定义>; }3.1类的继承与派生2023/11/1696继承方式分为三种关键字:publicprivate
protected;单继承(只有一个基类名)和多继承;该公司情况类代码:企业员工类相关派生类P79经理、销售人员、行政人员这三个派生类在声明语句中分别指定了它们的基类CCompanyStaff,以及它们的继承方式-public;CSaleManager类,同时继承经理类Cmanager和销售人员类Csaleman;3.1类的继承与派生2023/11/1697销售员经理行政主管行政人员企业员工类图3.1.3继承的级别增加行政主管一职,员工关系图如下:3.1类的继承与派生2023/11/1698用C++语言描述三个类的前向引用声明,形成了一个“类家族”:classCCompanyStaff;//基类员工//派生类行政人员继承员工classCAdminStaff:publicCCompanyStaff;
//派生类行政主管继承行政人员classCForeAdmin:publicCAdminStaff;基类不受派生类变化的影响;派生类继承了基类的全部数据成员和除了构造函数析构函数之外的全部成员函数,但是派生类能否访问这些成员还要受继承方式的约束;3.1类的继承与派生2023/11/16993.2继承方式3.2.1公有继承类的继承方式有public、private和protected三种,其中private是缺省的继承方式;公有继承:
基类各成员的访问权限如果是public或protected则在派生类中保持不变而基类中的private成员对派生类不可见,如图:2023/11/16100private基类Base图3.3公有继承中的访问控制protectedpublicprivate派生类Derv:publicBaseprotectedpublicDervobjDBaseobjB3.2继承方式2023/11/16101
例3.1一个公有继承的例子:sclass3_1.hsclass3_1.cppsmain3_1.cpp
基类A的所有成员在派生类中的访问控制权限都保持不变。3.2继承方式2023/11/161023.2.2私有继承:基类各成员无论是何种访问权限在派生类中一律以private的身份出现,连派生类的对象也无法访问,只有派生类的函数能在类内访问它们。若要私有继承来的某些基类的成员函数在派生类中也能对外可见,需在派生类中对其成员公有化。
usingCAnimal::SetWeight;//在派生类中将基类的成员函数SetWeight()公有化
usingCAnimal::GetWeight;//在派生类中将基类的成员函数GetWeight()公有化例3.2私有继承中的公有化:sclass3_2.hsmain3_2.cpp3.2继承方式2023/11/16103私有继承后的基类成员成为了派生类的私有成员,实际上相当于终止了基类功能的继续派生。class<派生类名>:private<基类名>{public:
using<基类名>::<基类函数名>;//公有化
……};3.2继承方式2023/11/161043.2.3保护继承:
protected访问权限: 设计基类时,在隐藏成员的同时还要允许派生类的成员能访问到,这时protected就比private更合适;
例3.3保护权限举例:sclass3_3.hsmain3_3.cpp3.2继承方式2023/11/16105使用protected修饰类成员存在的隐患,它破坏了封装;
protected继承;
保护继承下基类各成员的访问权限(除private外)都以protected权限出现;比较私有继承和保护继承,可以看出在直接派生类中两者的效果实际上都相同,但如果再继续派生下去就会出现区别;3.2继承方式2023/11/16106假设有A<-B<-C这样一个继承顺序如果B私有继承A后又派生出C那么C就无法再间接继承A的成员对A功能的继承在B那儿就终止了;而如果B保护继承A后又派生出C那么A中的公有和保护成员在B中都是保护成员因此A的功能可以被C间接继承;在实际开发时选择恰当的继承方式;3.2继承方式2023/11/161073.2继承方式3.2.4多继承(多个父类):声明多继承的语法如下:
class<派生类名>:<继承方式><基类名1>,<继承方式><基类名2>,…多继承举例:在公司员工管理中有时会存在多继承关系,如图:经理销售部经理销售人员2023/11/161083.3派生类的构造函数和析构函数3.3.1构造函数:在创建派生类的对象时,系统执行派生类的构造函数而不会自动执行基类的构造函数;在设计派生类的构造函数时,不仅要为初始化派生类新增加的数据提供参数,而且还应为初始化基类数据成员提供参数相关语法:
<派生类名>(<形参表>):<基类名1>(<形参表1>)<基类名2>(<形参表2>)
{ <派生类新增数据成员的初始化> }2023/11/161093.3派生类的构造函数和析构函数派生类构造函数的调用顺序为先父辈(基类数据)后自己(派生类新数据);轿车具有交通工具的一般特征,比如轮胎数目,同时又具有轿车自身的特征,如载客人数,因此轿车继承交通工具,成为它的一个派生类。例3.4派生类构造函数举例:sclass3_4.hsmain3_4.cpp2023/11/161103.3派生类的构造函数和析构函数派生类必须定义构造函数的两种情况:派生类新增的数据成员需要定义构造函数来为其初始化;基类定义了带参数的构造函数需要派生类为其提供参数以完成基类数据成员的初始化。2023/11/161113.3派生类的构造函数和析构函数3.3.2析构函数当派生类对象消亡时,系统会自动调用派生类的析构函数做一些必要的清理工作;由于在继承过程中派生类不能继承基类的析构函数,所以如果需要就只能在派生类中重新定义;2023/11/16112在执行派生类的析构函数时基类的析构函数也将被自动调用;析构函数调用的顺序是先派生类的析构函数,然后是基类的析构函数。例3.5派生类析构函数举例:
sclass3_5.hsmain3_5.cpp3.3派生类的构造函数和析构函数调用类A的构造函数调用类B的构造函数调用类B的析构函数调用类A的析构函数2023/11/161133.4虚基类3.4.1声明一个虚基类
多继承中的多义性
2023/11/161142.作用域分辨符可以通过作用域分辨符明确指定调用哪个类的接口;其语法形式如下:<派生类对象名>.<基类名>::<数据成员名>//访问数据成员<派生类对象名>.<基类名>::<成员函数名><参数表>//访问成员函数3.4虚基类2023/11/16115作用域分辨符举例:Smain3_5_B.cpp基类可以将其设为虚基类,这样它的数据成员在内存中就只有一个副本,成员函数也只有一个映射从而解决了同名成员的唯一标识问题;3.4虚基类2023/11/161163.虚基类由virtual标识,声明语法如下:class<派生类名>:virtual<继承方式><基类名>;例3.6虚基类举例:sclass3_6.hsmain3_6.cpp考虑员工管理系统中的多继承关系使用虚基类来定义公司员工管理系统中的多继承关系:3.4虚基类2023/11/16117//在继承路径“销售经理—>销售人员—>员工”中将基类CCompanyStaff设为虚基类classCSaleman:virtualpublicCCompanyStaff;//在继承路径“销售经理—>经理—>员工”中将基类CCompanyStaff设为虚基类classCManager:virtualpublicCCompanyStaff;//派生类CSaleManager成为两条继承路径的交汇点classCSaleManager:publicCSaleman,publicCmanager;3.4虚基类2023/11/16118比较使用作用域分辨符和虚基类技术:使用作用域分辨符时基类的成员在内存中存在多个副本,通过指定基类名来唯一标识使用的是哪个副本,这样可以存放不同的数据进行不同的操作;使用虚基类技术使基类的成员在内存中只存有一个副本;3.4虚基类2023/11/161193.4.2虚基类的初始化:虚基类的初始化在语法上和处理一般基类一样只是调用顺序略有不同它遵循以下顺序:1)虚基类的构造函数在非虚基类之前调用;2)若同一层次中包含多个虚基类则按它们的声明顺序调用;3)若虚基类由非虚基类派生而来要先调用更高级别基类的构造函数再遵循上述1和2的顺序。
3.4虚基类2023/11/16120虚基类初始化顺序的简单举例:classA;classB;classC:publicA,virtualB
{};将产生如下的调用次序B()
A()
C()3.4虚基类2023/11/16121较复杂的虚基类初始化举例:
3.4虚基类2023/11/16122为了避免在不同继承路径中继承来的同一成员发生多个副本的冲突可以将相应的基类设为虚基类,见源代码:sclass3_7.hsmain3_7.cpp运行结果:classBase1classBase2classLevel2classBase2classLevel1classLeaf3.4虚基类2023/11/161233.5聚合类3.5.1聚合类的概念设计孤立的类是较容易的,难的是正确设计基类及其派生类;一般地如果在逻辑上A是B的一部分而A与B又不属于同一类范畴,则不允许B继承A的功能而是要用A和其它东西组合出B;聚合(aggregation)也是实现程序代码重用的另一有效手段;类的聚合,就是指在一个类中内嵌其他类的对象作为成员的现象;“拥有”(hasa)关系,聚合类拥有内嵌对象;出于信息隐藏的考虑,常将其访问权限设为“私有”。2023/11/161243.5.2聚合类中的构造函数聚合类对象在创建时作为其组成部件的内嵌对象将首先被系统创建;如果一个聚合类,同时又是一个派生类,那么它的构造函数初始化列表还应负责为基类构造函数的调用提供参数;当一个类既是聚合类又是派生类时的构造函数定义形式:
<类名>::<类名><形参表><基类><形参表><内嵌对象><形参表> { 类的初始化 }3.5聚合类2023/11/16125例3.8公司的每一个员工都拥有不同的教育背景,其中记录着毕业学校、最高学历等基本数据。可以将教育背景抽象为一个类CEducation,拥有上述数据,并提供查询数据和显示数据等基本功能。企业员工类CCompanyStaff要使用教育背景类CEducation的功能,但是前者不能继承后者所具有的特征,因此可以把CCompanyStaff类处理成一个聚合类,在类中内嵌一个Ceducation类的对象。员工管理系统中的聚合关系:sclass3_8_companyStaff.hsclass3_8_companyStaff.cppsmain3_8.cpp
3.5聚合类2023/11/161263.6继承应用实例2.6.1问题描述:该公司的员工组成很简单,主要分成经理,销售部门经理,销售人员和行政人员等几类;所有员工都具有姓名,工作编号,基本工资,奖金,当月出勤记录等数据,都拥有一定的教育背景;每个员工都存在录入和显示信息等操作,并且需要根据考勤等实际因素来发放工资;不同类别的员工计算工资的办法也各不相同;
例3.9公司员工管理系统,源程序:sclass3_9_companyStaff.hsclass3_9_companyStaff.cppsmain3_9.cpp2023/11/16127习题课后习题、作业习题1习题2习题32023/11/16128Thanks!
TheEnd2023/11/16129函数重载第四章多态性132虚函数多态性4运算符重载内容提要2023/11/161304.1多态性基本概念及其实现方式多态性就是一个事物多种形态,就是同一符号或者名字在不同情况下具有不同解释的现象。多态性有两种表现形式:一种是不同的对象在收到相同的消息时,产生不同的动作;另一种是同一对象收到相同的消息却产生不同的函数调用。2023/11/161314.1.2多态的两种实现方式两种表现形式分别叫做:编译时多态和运行时多态。(重载)编译时多态——也叫静态多态性,属于早期绑定,在编译时就实现了绑定,它是静态联编的;运行时多态——也叫动态多态性,属于晚期绑定,在编译时还无法确定绑定对象,只有在运行时才能够实现绑定,它是动态联编的。通过虚函数实现,动态4.1多态性基本概念及其实现方式2023/11/16132以下两种情况不是动态联编的:1、在基类中未使用虚函数、纯虚函数。2、在基类中使用了虚函数和纯虚函数,但使用对象直接调用。这也不是动态联编的。使用虚函数、纯虚函数,使用基类的指针或者引用。
4.1多态性基本概念及其实现方式2023/11/16133“绑定”——就是让函数调用与函数体产生关联。在编译时就确定——叫“早期绑定”;在程序运行时才确定——叫“晚期绑定”。而C++的多态性在“早期绑定”和“晚期绑定”两方面都有体现。4.1多态性基本概念及其实现方式2023/11/161344.2虚函数虚函数是在基类中使用了关键字virtual的成员函数。虑函数与一般函数重载的区别:1、虚函数定义在基类和派生类中,函数原型完全一致;2、函数重载在同一个类中,或者都在类外定义,函数原型必定不完全相同。虚函数——运行时多态2023/11/161354.2.1虚函数的基本概念及其定义主要通过例子程序说明其机制.4.2虚函数2023/11/16136例4_1:利用对象分别调用不同的Show()——正确调用了特定的Show()函数。s4_1\sclass4_1_student.hs4_1\sclass4_1_student.cpps4_1\smain4_1.cppCPersonoCPerson("德华刘","男"); CStudentoCStudent(20050101,"学友张","男");oCPerson.Show(); oCStudent.Show(); 4.2虚函数2023/11/16137在main()中通过“对象.成员函数”的形式调用show(),分别调用了对象自己的show()。刘德华|男张学友|男|200501014.2虚函数2023/11/16138下面通过基类指针来调用,会有什么结果啦?例4_2:利用基类的指针调用不同的Show()。s4_2\smain4_2.cppch4_2\sclass4_2_student.hch4_2\sclass4_2_student.cpp刘德华|男张学友|男4.2虚函数无论让基类的指针指向基类对象还是派生类对象,系统都无法调用派生类对象oCStudent的Show()函数。实际调用的都是基类的Show()成员函数。
2023/11/16139通过基类的引用,引用派生类对象,会有什么结果啦?例4_3:利用基类的引用作为参数,调用不同的Show(),依然未达到目的。
s4_3\smain4_3.cppch4_3\sclass4_3_student.hch4_3\sclass4_3_student.cpp刘德华|男张学友|男4.2虚函数2023/11/16140无论引用基类对象还是派生类对象,函数内调用的都是基类的Show()成员函数。通过基类的引用去引用派生类对象,只能看到派生类从基类中继承而来的部分。这是由于C++的静态联编机制造成的。它首先将指向基类的指针与基类成员函数Show()连接在一起。这样,不管pCPerson指向哪个对象,pCPerson->Show()调用的总是基类的成员函数Show()。4.2虚函数2023/11/16141C++提供了虚函数(virtualfunction)机制解决上述问题。通过虚函数机制,实现了动态联编。一旦基类的成员函数定义为虚函数,则其派生类的同名成员函数(原型一致)不管前面是否加关键字virtual,同样具有虚特性,同样是虚函数。虚函数的传递性4.2虚函数2023/11/16142动态联编在多继承中尤其有用。但虚函数机制也是有缺陷的,为了实现虚特性需要增加一些数据存储和执行指令的开销,虚函数的使用也不是越多越好。将一个类中的所有成员函数都定义为虚函数(virtual)也是可行的,它除了会增加一些额外的开销外,没有其它更多的坏处,虚函数对于保证类的封装特性是有好处的。4.2虚函数2023/11/16143虚函数的“动态联编虚特性”必须通过基类的指针或者基类的引用来表现。例4_4:定义了两个CPerson的继承类,具有虚函数的两个实现版本。s4_4\sclass4_4_student.hs4_4\sclass4_4_student.cpps4_4\smain4_4.cpp虚函数要求原型一致4.2虚函数2023/11/161441:-通过基类的引用调用具有虚特性----德华刘|男学友张|男|20050101孔老师|男|50002:-通过基类指针调用具有虚特性----德华刘|男学友张|男|20050101孔老师|男|50003:-通过对象调用则不具有虚特性----德华刘|男学友张|男|20050101孔老师|男|50004.2虚函数2023/11/16145在例4_4中,其他代码不变,将CPerson类声明部分的“virtualvoidShow()const;”函数定义语句去掉关键字virtual;改为“voidShow()const;”请问程序的运行结果会如何?4.2虚函数2023/11/161461:-通过基类引用调用(不)具有虚特性---德华刘|男学友张|男孔老师|男2:-通过基类指针调用(不)具有虚特性---德华刘|男学友张|男孔老师|男3:-通过对象调用则不具有虚特性---德华刘|男学友张|男|20050101孔老师|男|50004.2虚函数2023/11/16147虚函数的定义要遵循以下几条重要规则:1、原型不一致,即使加上了关键字virtual,也不会进行滞后联编。2、类的成员函数才能说明为虚函数。3、静态成员函数不能是虚函数,静态成员函数是不受某个对象的限制,它属于类。虚函数必须是成员函数4.2虚函数2023/11/161484、内联(inline)函数不是虚函数,内联函数不能在运行时动态确定位置,它在编译时采用插入函数体的方式处理。。5、构造函数不虚函数,构造对象的时候,对象还是一片未定型的空间,只有对象构造完成后,对象才是具体类的实例。6、析构函数可以是虚函数,而且通常将析构函数声明为虚函数。有儿子,则其析构函数需要是虚函数4.2虚函数2023/11/161494.2.2虚函数与重载函数的关系1、重载函数函数名称相同,参数不同;重载函数是在作用域相同的区域里定义的相同名字的不同函数.2、虚函数函数原型完全一致,体现在基类和派生类的类层次结构中。3、重载函数可以是成员函数或友员函数,而虚函数只能是成员函数;4、调用重载函数以所传递参数序列的差别作为调用不同函数的依据;虚函数则根据对象的不同调用不同类的虚函数;5、重载函数在编译时表现出多态性,是静态联编;而虚函数在运行时表现出多态性,是动态联编,动态联编是C++的精髓。4.2虚函数2023/11/161504.2.3虚函数与函数隐藏和函数覆盖基类和派生类中的原型一致的函数,如果是虚函数,则称为覆盖;如果原型一致的函数不是虚函数,就称为隐藏。原型不一致的话,不管是否虚函数,都称为隐藏。虚函数才有可能覆盖4.2虚函数2023/11/16151覆盖和隐藏适用下述规则(基类和派生类):
1、完全同名,参数不同,不论是否虚函数,基类的函数将被隐藏。2、如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数不是虚函数。此时,基类的函数被隐藏。3、如果派生类的函数与基类的函数同名,并且参数也相同,基类函数也是虚函数。此时,基类的函数被覆盖了。4.2虚函数2023/11/16152另外,当基类中的虚函数和派生类中的函数原型出现不同时,将按照以下两种情况处理:1、仅仅返回值类型不同,其余均相同,系统会当作错误处理。仅仅返回值不同的函数本质上是含糊的。2、函数原型不同,仅函数名相同。此时系统将认为它是重定义函数,将丢失虚特性,且该函数还会隐藏基类同名函数。4.2虚函数2023/11/16153例4_5:重载、隐藏和覆盖。
s4_5\sclass4_5_student.hs4_5\sclass4_5_student.cpps4_5\smain4_5.cpp函数名相同,原型不同的虚函数或非虚函数,在派生类中都被隐藏;原型相同的虚函数在派生类中被覆盖;原型相同的非虚函数在派生类中被隐藏。4.2虚函数2023/11/161544.2.4多重继承中的虚函数虚函数具有传递性,具有虚函数的基类指针可以指向它的派生类,也可以指向它的派生类的派生类,它们都具有动态特性。为了实现多重继承中虚函数的传递性,在派生时,需要采用public继承方式,否则无法实现虚特性.另外,多重继承中,需要采用虚基类(virtual)的继承关系.virtualpublic4.2虚函数2023/11/16155例4_6:多重继承中的虚函数
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 6,7附肢骨幻灯片
- 《资金需要量的预测》课件
- 大型设备电控复习试题附答案
- 《露沙尼亚讲》课件
- 《领导科学》课件
- 小学低段古诗整合教学的策略
- 《封装工艺流程》课件
- 昆虫记中的生物世界解读
- Unit 4 Information Technology Lesson1 Avatars Reading 逐字稿说课稿-2024-2025学年高中英语北师大版(2019)必修第二册
- 《有效教学的策略》课件
- 公司年度亏损专项治理方案
- 皮肤科疑难病例讨论课件
- 通信系统防雷与接地下篇的知识
- Q∕GDW 12118.2-2021 人工智能平台架构及技术要求 第2部分:算法模型共享应用要求
- 管理者完成目标的五步19法姜洋讲义
- 亳州市污水处理厂工艺设计
- 小学三年级下册综合实践活动.水果拼盘-(14张)ppt
- 复查(复核)信访事项流程图
- the sad young men上课
- 年晋煤集团薪酬管理办法
- 机动车驾驶员培训学校管理制度汇编
评论
0/150
提交评论