第2章 面向对象程序设计基础_第1页
第2章 面向对象程序设计基础_第2页
第2章 面向对象程序设计基础_第3页
第2章 面向对象程序设计基础_第4页
第2章 面向对象程序设计基础_第5页
已阅读5页,还剩70页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

VisualC++实用教程第2章面向对象程序设计基础1教学目标:了解面向对象的基本概念

熟练掌握类、对象、派生类的定义和使用

掌握类的构造函数和析构函数的定义和特点

能够利用虚函数实现多态性熟悉友元的特性

2教学内容:2.1面向对象的基本概念2.2类和对象的定义2.3继承性和派生类2.4多态性2.5友元2.6模板32.1

面向对象的基本概念观点:自然界是由实体(对象)所组成。程序设计方法:使用面向对象的观点来描述、模仿并处理现实问题。要求:高度概括、分类、和抽象。目的:实现软件设计的产业化。程序=多个对象+消息4类和对象对象是现实世界中一个实际存在的事物对象的静态特征对象的行为类是具有相同属性和行为的一组对象的集合,它为属于该类的全部对象提供了统一的抽象描述,其内部包括属性和行为两个主要部分。对象之间的交互:发送消息5封装封装的目的是隐藏对象的内部的实现细节。通过封装,可以将对象的外部接口与内部的实现细节分开。

目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只需要通过外部接口,以特定的访问权限,来使用类的成员。6继承继承是C++中支持层次分类的一种机制,允许程序员在保持原有类特性的基础上,对新类进行更具体的说明。实现:声明派生类——2.37多态性多态:同一名称,不同的功能实现方式。目的:标识统一,减少程序中标识符的个数;接口统一,增加程序的灵活性实现:——2.4编译时的多态性:重载函数运行时的多态性:虚函数82.2类与对象的定义类是C++的灵魂,如果不真正掌握类,就不能真正掌握C++C++中的类就是一种用户自己定义的数据类型,和其它数据类型不同的是,组成这种类型的不仅可以有数据,而且可以有对数据进行操作的函数。为了封装的需要,一般将类的定义放在一个.h文件中,而将类的成员函数的实现放在一个.cpp文件中。92.2.1类的定义class类名称{

public:

公有成员(外部接口)

private:

私有成员

protected:

保护成员};类与外部的接口只允许本类中的函数访问(可省略)只能由本类及其派生类的成员函数访问是必需的成员访问说明符10例2.1:圆类的定义见教材24页11类的数据成员(成员变量):与一般的变量声明相同,但需要将它放在类的声明体中,一般为私有访问属性。类的成员函数定义:在类中说明原型,类外给出函数体实现,函数名之前必须加类名和作用域区分符限定将成员函数定义为内联函数:成员函数在类的内部定义,此时无须使用inline关键字,成员函数自动为内联函数。成员函数在类内声明,在类外定义。但在类外定义时,要加关键字inline。定义与声明放在同一.h文件中。说明:12const成员函数const成员函数:不修改数据成员的值,在程序中如果不小心修改了这个成员函数中的对象,则编译器会产生一个语法错误信息。const成员函数的定义方法:在函数的原型和定义中,在函数参数表和函数定义的左花括号之间加入const关键字。类的成员函数允许重载,允许带缺省参数值。132.2.2构造函数性质:与类同名、无返回类型在对象创建时由系统自动调用允许重载、带缺省值如果类中未声明,则系统自动生成一个缺省形式的构造函数,形如:类名(){}作用:对象的初始化14构造函数举例---CCircle类classCCircle{public:

CCircle()//无参(缺省)构造函数

{ radius=1;}CCircle(doubler)//带参构造函数

……private: doubleradius;};CCircle::CCircle(doubler){radius=r>0?r:1;}两个构造函数合成一个带缺省值的构造函数CCircle(doubler=1);//构造函数原型CCircle::CCircle(doubler)//构造函数定义{radius=r>0?r:1; }15拷贝构造函数拷贝构造函数是一种特殊的构造函数,其形参为本类对象的引用。class类名{public:

类名(形参);//构造函数类名(类名&对象名);//拷贝构造函数

……};类名::类名(类名&对象名)//拷贝构造函数的实现{函数体}16拷贝构造函数举例classCCircle{public:

CCircle(floatm=1,floatn=1,floatr=1);

CCircle(constCCircle&c)//拷贝构造函数

{ radius=c.radius; }……};17当用类的一个对象去初始化该类的另一个对象时系统自动调用它实现对象的拷贝赋值。voidmain(){

CCirclec1;

CCircle

c2(c1);//拷贝构造函数被调用}拷贝构造函数调用之一18拷贝构造函数调用之二若函数的形参为类对象按值传递时,实参赋值给形参,将自动调用复制构造函数,例如:voidfun1(CCirclec){

cout<<c.GetRadius()<<endl;}voidmain(){

CCirclec1(2,2,10);fun1(c1);//调用复制构造函数

}

19当函数的返回值是类对象时,系统自动调用复制构造函数。例如:CCirclefun2(){

CCirclec2(10);returnc2;//调用复制构造函数}voidmain(){

CCirclec3;c3=fun2();}拷贝构造函数调用之三20对于任何类,如果程序员没有定义拷贝构造函数,则系统自动生成一个拷贝构造函数,这种拷贝构造函数在执行时,只是进行简单的成员复制,这对于含有指针成员的类是非常危险的,在这种情况下,必须自定义拷贝构造函数。拷贝构造函数说明212.2.3析构函数作用:是释放对象所占的动态内存空间。

特点:函数名:~类名();没有参数,也不返回任何值不允许重载如果类中未定义析构函数,编译器将自动生成一个缺省的析构函数。形如:~类名(){}22在下列两种情况下会被自动调用:

对象定义在一个函数体中,该函数调用结束后,自动调用析构函数;用new生成的动态对象,当使用delete删除时,自动调用析构函数。通常利用析构函数删除对象中指针成员所指向的动态存储空间,当类中没有指针成员时,则不需要定义析构函数。析构函数的调用232.2.4对象的定义和使用类的对象是该类的某一特定实体,即类类型的变量。声明形式:

类名对象名;例:

CCircleC1;

CCircle*pCircle;

CCircle

ArrayCircle[3];24类成员的访问方式类内成员互访直接使用成员名可以访问任意属性成员类外访问只能访问

public

属性的成员使用“对象名.成员名”方式使用“对象指针名->成员名”方式25classTime{public:

inthour;

intminute;};Timet,*p;p=&t;cout<<p->hour;p->hour(*p).hourt.hour26this指针在C++中为每个非静态成员函数提供了一个名字为this的指针,当进行成员函数调用时,系统自动将调用此成员函数的对象的地址作为一个隐含的参数传递给this指针,即让this指针指向调用此成员函数的对象,从而使成员函数知道该对哪个对象进行操作。使用this指针,保证了每个对象可以拥有不同的数据成员,但处理这些数据成员的代码可以被所有的对象共享。27例2.2类的应用举例(教材28页)一圆型游泳池如图所示,现需要在其周围建一圆型过道,并在其四周围上栅栏。栅栏价格为35元/米,过道造价为20元/平方米。过道宽度为3米,游泳池半径由键盘输入。要求编程计算并输出过道和栅栏的造价。游泳池过道282.2.5静态类成员

创建了一个对象,则这个对象将拥有所有类中的成员,如果某个数据对所有对象都一样,则这个数据只要有一个拷贝,而不是每个对象都重复定义这个数据,形成内存浪费。简言之:一个数据拷贝,所有对象共享。这时可以使用静态数据成员。静态类成员:使用关键字static进行修饰的类成员。静态类成员可以声明为public、private或

protected。

291、静态数据成员表示的是类范围中所有对象共享的信息,相当于局部于类中的“全局变量”,为该类的所有对象共享。因为静态数据成员只有一个数据副本,所以可以节省存储空间。2、静态数据成员必须在文件作用域内进行初始化,而且只能初始化一次。1、静态数据成员3、由于数据隐藏的需要,静态数据成员通常被声明为

私有的,而通过定义公有的静态成员函数来访问静态数据成员。30静态数据成员的定义和初始化classCCircle{public:……private:……

staticint

NumOfObject;//程序中生成的对象的个数};intCCircle::NumOfObject=0;//静态数据成员的初始化31

静态成员函数:是用关键字static进行修饰的成员函数。

静态成员函数没有this指针,因此当其访问非静态数据成员时,必须通过对象名或对象指针访问。2、静态成员函数定义格式:static成员函数的原型;32使用静态类成员(教材30页)

3、静态成员的访问在类内:可直接访问。在类外:只能访问公有静态成员类名::成员名(常用)对象名.成员名(.和->)332.3继承性和派生类继承是面向对象程序设计的最重要的特点之一,是软件重用的一种重要形式,是对实际问题中分层特性的一种自然描述。继承的实质:是从已有的类建立新类。通过继承,派生类自动拥有基类的所有成员(数据成员和成员函数)基类和派生类单继承和多继承

342.3.1派生类的定义class派生类名:继承方式基类名{

成员声明;};35继承方式三种继承方式:public(公有)、

private(私有)、

protected(保护),

表2-1继承方式对派生类的影响继承方式说

明public(公有)基类的public和protected成员被派生类继承后,保持原来的访问属性不变private(私有)基类的public和protected成员被派生类继承后,变成派生类的private成员protected(保护)基类的public和protected成员被派生类继承后,变成派生类的protected成员36例2.3

类的派生示例教材31页

372.3.2派生类的构造函数与析构函数基类的构造函数不能被继承,需要在派生类中负责基类成员的初始化。通过在成员初始化表中显式调用基类的构造函数来实现的单一继承时的构造函数派生类名::派生类名(基类成员和本类成员所需的形参):基类名(实参){

本类成员初始化;}38派生类构造函数与析构函数的执行顺序在创建派生类的对象时,派生类的构造函数总是先调用其基类的构造函数来完成基类成员的初始化。如果在基类和派生类中都包含其他类的对象(即有对象成员),则在创建派生类的对象时,首先执行基类的对象成员的构造函数,接着执行基类的构造函数,然后执行派生类的对象成员的构造函数,最后才执行派生类的构造函数。当派生类对象的生命周期结束时,析构函数的执行顺序和构造函数的执行顺序正好相反。392.3.3多继承如果一个派生类有多个直接基类,则称为多继承。多继承意味着一个派生类可以继承多个基类的成员,这种强大的功能支持了软件的重用性,但可能会引起大量的二义性(歧义性)问题。

401.多继承的定义格式class<派生类名>:[<继承方式>]<基类名>,[<继承方式>]<基类名>,…{[<派生类的成员>]};其中,继承方式的使用与单继承完全相同。【例2.4】使用多继承(教材35页)412.多继承派生类对象的初始化派生类构造函数名(总参数表列):基类1构造函数(参数列表),基类2构造函数(参数列表),基类3构造函数(参数列表){派生类中新增数据成员初始化语句}先调用基类的构造函数,再执行派生类构造函数的函数体。调用基类构造函数的顺序是按照声明派生类时基类出现的顺序。423.二义性问题因不同基类中可能含有同名成员引起的二义性:用基类名加以限定,形如:基类名::同名成员名,以明确指出所使用的成员是从哪个基类继承来的。因公共基类使得派生类中含有同名成员,因此也会产生二义性。为了消除这种二义性,也必须使用基类名限定所访问的同名成员。43虚基类解决多继承中因公共基类而产生的二义性问题。声明虚基类的一般格式为:class<派生类名>:virtual[<继承方式>]<基类名>{[<派生类的成员>]};保留字virtual和继承方式的相对位置无关紧要,但要放在基类名之前,并且virtual只对紧跟其后的基类名起作用。442.4多态性在面向对象的程序设计中,把不同类的对象收到相同的消息时产生多种不同的行为方式称为多态性。452.4.1两种多态性联编:将一个函数调用链接上相应于函数体的代码,这一过程称为函数联编(简称联编)。静态联编:在编译阶段完成;优点是执行速度快动态联编:在运行时才能把函数调用与函数体联系在一起。具有灵活性高,易于扩充和维护等优点,但运行效率较低。静态联编所支持的多态性称为编译时的多态性,通过函数重载来实现。动态联编所支持的多态性称为运行时的多态性,通过虚函数来实现。462.4.2编译时的多态性【例2.6】编译时的多态性(教材39页)472.4.3虚函数虚函数是实现动态联编的基础。虚函数的声明:在函数原型之前加virtual。class<类名>{public:

//虚函数的声明

virtual<返回类型><函数名>(<参数表>);};

//虚函数的定义<返回类型><类名>::<函数名>(<参数表>)

{…...}

48应注意的问题:应该在类层次结构中需要多态性的最高层类内声明虚函数。派生类中与基类虚函数原型完全相同的成员函数,会自动成为虚函数。不能把静态成员函数、构造函数和全局函数声明为虚函数。析构函数可以声明为虚函数。通过声明虚函数来使用C++提供的多态性机制时,派生类应该从它的基类公有派生。

492.使用虚函数必须合理调用虚函数才能实现动态联编。只有使用基类类型的指针或引用调用虚函数时,系统才以动态联编方式实现对虚函数的调用,才能获得运行时的多态性。通常都用指向第一次定义虚函数的基类对象的指针或引用来调用虚函数。虚函数的使用(教材41页)503.纯虚函数纯虚函数:在声明时“初始化值”为0的函数。class<类名>{public:virtual<返回类型><函数名>(<参数表>)=0;};纯虚函数不需要进行定义51抽象类带有纯虚函数的类称为抽象类。不能声明抽象类的对象,但可以声明抽象类的指针和引用。抽象类只能作为基类来使用。522.5友元类的重要特性是对数据实现了封装和隐藏,这样能大大提高程序的质量,特别是能够提高软件的可维护性。封装也会带来一些不便。如果在程序中为了访问对象的私有数据成员而频繁调用公有成员函数时,将会带来较大开销,从而降低程序的执行效率,影响程序的性能。C++中引入了友元机制,一个类可以赋予某些函数或类(友元函数或友元类)访问它的私有成员的特权,从而减少了开销。532.5.1友元函数能够访问一个类的私有部分而又不是该类成员函数的函数,称为该类的友元函数。将一个函数声明为类的友元,就是在类定义中该函数的原型前面加上关键字friend。如下所示:classA{friendvoidsetX(A&,int

);…};54友元函数说明友元函数虽然在类内声明,但它不是该类的成员函数,所以友元函数没有this指针。友元关系的声明与成员访问说明符private、protected和public无关,因此友元函数声明可以放在类定义中的任何地方。【例2.8】使用友元函数计算两点间的距离。55友元成员一个类的成员函数也可以声明为另一个类的友元函数。把类C1的成员函数func声明为类C2的友元函数:classC1{

…voidfunc(…);

…};classC2{friendvoidC1::func(…);//声明友元成员

…};voidC1::func(…)

//定义友元成员{…}562.5.2友元类如果一个类的所有成员函数都可以访问另一个类的私有成员,则可以把这个类声明为另一个类的友元类。例如,把类C1声明为类C2的友元类:

classC2{friendclassC1;

…};【例2.9】使用友元类(教材47页)572.6模板模板是C++实现软件重用的一种形式,是C++最强大的特性之一。利用模板,我们可以用一段代码指定一组相关函数(称为模板函数)或一组相关类(称为模板类)。这样能大幅度地节约程序代码,显著减少冗余信息,从而进一步提高面向对象程序的可重用性和可维护性。模板的功能很强,用户既可以定义类模板,也可以定义函数模板,还可以使用C++标准模板库(STL)中已有的模板。

582.6.1函数模板如果几个函数的功能相同,实现算法也相同,只是所处理的数据的类型不同,使用函数模板更简洁,更方便。1.函数模板的定义

template<class<数据类型参数表>>

函数模板定义体59说明template是C++的保留字,表示后面定义的是一个模板。<数据类型参数表>形如<classT1,classT2,…>,T1,T2等是类型参数(形式参数),函数模板定义体与普通函数的定义相同,只不过其中的有些数据类型,例如返回值类型,形参的类型,局部变量的类型等,要使用类型参数表中的标识符T1,T2等表示。602.函数模板的调用函数模板的调用格式有两种:第一种格式:函数模板名(实参表)第二种格式:函数模板名<类型实参

>(实参表)函数模板的调用过程:函数模板实例化,把模板的类型参数T1,T2等用具体的数据类型去替换。函数模板实例化后会得到一个具体的函数,该函数称为模板函数。执行模板函数,完成所需要的功能。【例2.10】利用函数模板实现求两个数据的较大值。(教材50页)613.函数模板的特点函数模板实际上代表了一组函数,而不是一个具体函数。所以,函数模板必须先实例化,才能完成具体函数的功能。函数模板不具有隐式类型转换的能力。普通函数在进行调用时,如果实参的类型与形参的类型不同,则系统会自动对参数类型进行隐式转换,将实参的值转换为函数所需的类型(实际上是生成一个临时值使用),然后再进行函数调用。而函数模板不具有这种功能。

622.6.2类模板1.定义类模板(1)定义类template<class<类型参数表>>class<类名>{…};(2)定义成员函数632.使用类模板类模板与函数模板一样,也是代表一组类。因此在使用类模板时首先要把它实例化为一个具体的类,这个具体的类称为模板类。把类模板实例化为模板类的格式如下:类名<具体数据类型名>然后用模板类声明对象并使用这些对象完成所需要的功能。64#include<iostream>usingnamespacestd;template<classnumtype>classCompare{public:

Compare(numtype

a,numtypeb){x=a;y=b;}

numtypemax(){return(x>y)?x:y;}

numtypemin(){return(x<y)?x:y;}private:

numtype

x,y;};65intmain(){Compare<int>cmp1(3,7);

cout<<cmp1.max()<<"istheMaximumoftwointedernumbers."<<endl;

cout<<cmp1.min()<<"istheMinimumoftwointedernumbers."<<endl<<endl;Compare<float>cmp2(45.78,93.6);

cout<<cmp2.max()<<"istheMaximumoftwofloatnumbers."<<endl;

cout<<cmp2.min()<<"istheMinimumoftwofloatnumbers."<<endl<<endl;Compare<char>cmp3('a','A');

cout<<cmp3.max()<<"istheMaximumoftwocharacters."<<endl;

cout<<cmp3.min()<<"istheMinimumoftwocharacters."<<endl;return0;}663.模板与继承(1)从类模板派生出类模板

template<classT>classCBase{

…};template<classT>classCDerived

:publicCBase<T>{

…};(2)从类模板派生出普通类template<classT>classCBase{

…};classCDerived

:publicCBase<int>{

…};67——获取水仙花数

1.项目要求编写一个程序,获取指定数是否为水仙花数。水仙花数应该满足下面条件:一个3位数,其各位数字的立方和等于该数本身。例如,153是一个水仙花数,因为153=13+53+33。该程序要求可以判断用户输入的数是否为水仙花数,也可以获取指定范围内的数(在100至10000内)是否为水仙花数。2.项目分析如果判断一个数是否是水仙花数,需要获取该数每位的数值。这可以通过求余数的方法获取每位的数值,然后获取每位的立方并求和。通过将和值与该数进行比较,可以判断该数是否为水仙花数。

68——获取水仙花数

3.项目实现创建一个工程Exam,该程序由类Data判断数是否为水仙花数,并输出。这就需要创建类Data,该类拥有两个私有属性:num和ntype。num为用户输入的数,ntype为用户选择的操作类型。ntype有以下两种类型。判断用户输入的数是否为水仙花数。获取指定范围内(在100~10000内)的水仙花数。

69类Data含有5个成员函数,说明如下。Data(intnum):Data构造函数,参数num为用户输入的数。该函数需要初始化ntype为1。Data():Data构造函数,获取指定范围内(在100~10000内)的水仙花数。该函数需要初始化ntype为2。GetResult():函数依据ntype执行相应的操作,并输出结果。IsSpecial(int

nNum):判断nNum是否为水仙花数,是则返回t

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论