程序的类层次结构课件_第1页
程序的类层次结构课件_第2页
程序的类层次结构课件_第3页
程序的类层次结构课件_第4页
程序的类层次结构课件_第5页
已阅读5页,还剩69页未读 继续免费阅读

下载本文档

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

文档简介

程序的类层次结构

程序的类层次结构类的继承与派生类层次中成员函数名的多态性派生面向对象的程序设计的一个重要特点是允许以既有类(也称基类),以其为基础导出(定义)新的类(也称派生类)。这一过程称为派生派生时不需要把既有类的相关代码重新书写一遍,只需要指明是以哪个类为基类,便可以将基类中的有关特征继承过来,实现了部分代码的可重用。类的继承与派生派生方式派生类的构造函数与释放函数多基派生虚基类类层次中成员名的作用域类层次中的类转换派生方式public派生与private派生Protected成员与protected派生public派生与private派生C++允许程序员用下边的格式用一个类派生它的子类:

class派生类名

:派生方式

基类名

{

private:

新增私有成员声明语句表列

public:

新增公开成员声明语句表列

};两种派生方式的特点1)无论哪种派生方式,基类中的private成员在派生类中都是不可见的。也就是说,基底类中的private成员不允许外部函数或派生类中的任何成员访问。2)public派生时,基类中的public成员相当于派生类中的public成员。3)private派生时,基类中的public成员相当于派生类中的private成员派生后基类成员访问性的变化基类成员的访问性privatepublic派

式privatepublicprivatepublic派

员不可见可见可见外

数不可见可见定义Location—Point类层次结构

classLocation{ //基类接口定义public:

int

mX_Pos,mY_Pos; //位置的坐标,以像素点计

Location(intx,inty); //构造函数,初始化位置坐标

int

getX(); //返回当前位置的x坐标

int

getY(); //返回当前位置的y坐标

};

enumBOOLEAN{ FALSE,TRUE}; //定义一个布尔类型

classPoint:privateLocation{ //派生类接口定义

BOOLEANmVisible; //可见性变量

Public:

Point(intx,inty); //构造函数,初始化点的位置

BOOLEAN isVisible();//返回当前点是否可见

Void show(); //显示当前点

Void hide(); //隐藏当前点

Void moveTo(); //移动当前点

};Protected成员与protected派生protected成员是一种血缘关系内外有别的成员。它只为它所在类中的方法和由它直接派生的类方法可见private、protected,、public作为类成员的可见性修饰符,将产生如下影响:

1)在一个类中定义的方法函数,可以访问本类中的任何成员,但只能访问基类中的protected成员和public成员;

2)一个类对象,只能使用本类或其public派生基类中的public成员例5.1.2classb{protected:

intx1;public:

intx2; b(){x1=x2=5;}

};classd1:publicb{public:

intfd11(){ returnx1; //ok,可以访问基类protected成员

returnx2; //ok,可以访问基类public成员

}

intfd12(){ bbb; //returnbb.x1;类对象不可使用本类protected成员

returnbb.x2; //ok,类对象可以使用基类public成员

}};定义Location—Point—Circle类层次结构

classLocation //位置类{ protected: //保护类成员

int

mX_Pos,mY_Pos; //在Location派生类中可访问public:Location(intx,inty);

int

getX();

int

getY(); };

enumBOOLEAN{ FALSE,TRUE}; classPoint:publicLocation //点类{ protected: //保护类成员

BOOLEANmVisible; //在Point派生类中可访问Public:

Point(intx,inty); BOOLEAN isVisible(); Void show(); Void hide(); Void moveTo(); };classCircle:publicPoint //圆类{protected: //保护类成员

int

mRadius; //圆半径,在Circle派生类中可访问Public:

Circle(intx,inty,intr); //初始化圆

void show(); //画圆

void hide(); //隐藏圆

void moveTo(); //移动圆

void expand(intdelta); //放大圆,半径为(r+delta)

void contract(intdelta); //缩小圆,半径为(r-delta)

};派生类中访问属性发生如下变化1)基类的private成员在派生类中是不可见的2)private派生使基类中的非private成员都成为派生类中的私有成员;protected派生使基类的非private成员在派生类中都变为protected成员;public派生使基类的非private成员在派生类中访问属性保持不变3)不同的派生方式引起成员访问属性的改变,只能降低,不能提高(如将private成员提升为protected成员或public成员)。但是,C++允许在派生类中用访问属性修饰符,恢复一个成员原来的访问属性例5.1.4classb{protected:

intx1;

intx2;public:

intx3;

intx4;…

};classd:privateb //x1,x2,x3,x4将派生为private成员{ protected:b::x1; //将x1恢复为protected成员

public:b::x3; //将x3恢复为public成员

…};派生类的构造函数与释放函数类层次结构中数据成员的存储派生类中构造函数/释放函数的定义与调用派生类对象的创建类层次结构中数据成员的存储Location—Point—Circle类层次结构中数据成员的存储mX_PosmY_PosmX_PosmY_PosmVisiblemX_PosmY_PosmVisiblemRadius构造函数/释放函数的定义与调用首先通过派生类的构造函数调用基类的构造函数,对基类成员进行初始化然后再执行对在派生类中新增的成员进行初始化的操作因此可以说,派生类对象是由其所有先辈类共同创建的派生类的构造函数原型格式

Y::Y(argX1,argX2,…,argY1,argY2,…):X(argX1,argX2,…)其中,X为Y的直接基类名,Y为派生类名。这一格式是递归的类层次结构Location—Point—Circle中的构造函数Location::Location(intx,inty){

mX_Pos=x;mY_Pos=y;}

Point::Point(intx,inty):Location(x,y)//先调用基类构造函数{ visible=FALSE; //缺省情况下是不可见的}Circle::Circle(intx,inty,intr):Point(x,y)//先调用基类构造函数{

mRadius=r;}说明1)创建一个Circle类对象时,要自动调用Point类的构造函数,再由Point类的构造函数调用其父类Location构造函数2)当一个派生类对象撤销时,释放函数调用的顺序与构造函数相反3)保护成员具有良好的继承性,对保护成员的初始化最好在基类中进行4)派生类的构造函数的构成形式,与包含对象成员的类的构造函数相似但不相同派生类对象的创建派生类对象可以用两种方式创建:

1)用常数参数表创建

2)由部分基类对象创建派生类对象多基派生派生类只有一个基类时,称为单基派生。一个派生类具有多个基类时,称为多基派生或多重继承(multipleinheritance),这时将继承每个基类的部分代码。多基派生是单基派生的扩展。与单基派生相比,既有同一性,又有特殊性。单基派生则可以看成多基派生的特例例由Hard(机器名)类与Soft(软件,由os与Language组成)类派生出System类

#include<iostream.h>#include<string.h>classHard{protected:charbodyname[20];public:Hard(char*bdnm)//构造函数

{cout<<"conH\n";

strcpy(bodyname,bdnm); }

Hard(Hard&abody)//复制构造函数

{cout<<"copyH\n";

strcpy(bodyname,abody.bodyname); } voidprint(){cout<<"Body_name:"<<bodyname<<endl; }};

classSoft{protected:

charos[10];charlang[15];public:Soft(char*o,char*lg) //构造函数

{cout<<"conF\n";strcpy(os,o);strcpy(lang,lg);}Soft(Soft&asoft) //复制构造函数

{

cout<<"copyF\n";

strcpy(os,asoft.os);

strcpy(lang,asoft.lang);}voidprint(){cout<<"os:"<<os<<",language:"<<lang<<endl;}};classSystem:publicHard,publicSoft //派生类System{ charowner[10];public:System(char*ow,char*bn,char*o,char*lg):Hard(bn),Soft(o,lg) //调用基类构造函数

{cout<<"conS\n";strcpy(owner,ow);}System(Hardabody,Softasoft,char*ow):Hard(abody),Soft(asoft) //调用基类复制构造函数

{cout<<"copyS\n";strcpy(owner,ow);}voidprint(){cout<<"owner:"<<owner;

cout<<";\nhard:"<<bodyname;

cout<<";\nsoft:"<<os<<","<<lang<<endl;}};测试程序

voidmain(){Systembsystem("Wang",//用常参数表创建派生类对象

"IBMPC","PCDOS",

"TrueBASIC");

bsystem.print();

cout<<"Ok!\n";[y1]

Hardabody("AST386sx/16");Softasoft("PC

DOS","BorlandC++");Systemasystem("AST386sx/16",//基类对象创建派生类对象

asoft,"Zhang");

asystem.print();}虚基类在多基派生中,如果在多条继承路径上有一个公共的基类(如图5.5(a)中的base0),则在这些路径的汇合点(如图5.5(a)中的derived类对象),便会产生来自不同路径的公共基类的多个拷贝,如图5.5(b)所示。如果只想保留公共基类的一个拷贝,就必须使用关键字virtual把这个公共基类定义为虚基类在多条继承路径上公共基类的情形base0base1base2derivedbase0{a}base0{a}base1base2derived在定义基类直接派生的类时说明格式class派生类名

:virtual派生方式

基类名

{

};例5.1.7classbase0{public:

inta;…};classbase1:virtualpublicbase0{…};classbase2:virtualpublicbase0{…};classderived:publicbase1,publicbase2{…};几点说明1)virtual也是“派生方式”中的一个关键字,它与访问控制关键字(public或private,protected)间的先后顺序无关紧要2)为了保证虚基类在派生类中只继承一次,就必须将其直接派生类定义时都说明为虚拟派生;否则除从用作虚基类的所有路径中得到一个拷贝外,还从其它作为非虚基类的路径中各得到一个拷贝3)虚基类对象的初始化图5.6a所示的类层次结构,当A与B都是C与D的虚基类时,系统将要自左向右按深度优先遍历算法对公共派生类E进行初始化虚基类对象的初始化CBADEABABCDE类层次中成员名的作用域private成员的作用域只在本类对象内部public成员和protected成员,在类层次结构中将形成成员名多重作用域:

·基类成员作用域和派生类新增成员作用域形成相包含的关系,派生类新增成员作用域为内层,基类成员作用域在外层

·内层声明的标识符可以覆盖外层声明的同名标识符,使其不可见;对于多基派生类,如果其多个基类中拥有同名成员,而在派生类中又新增了同名成员,则派生类的该同名成员将覆盖所有基类的同名成员

·如果在几个基类中具有同名成员,但在派生类中没有与它们同名的新增成员名,则由于这些成员名具有相同的作用域,使系统无法判定到底是调用哪个基类成员有类层次结构:A←B←C#include<iostream.h>classA{public:

int

x,a;voidfun(inti){x=i;

cout<<"A::x="<<x<<endl;

cout<<endl;}};classB:publicA{public:

int

x,b;voidfun(inti){x=i*a; //引用非同名基类成员

cout<<"B::x="<<x<<endl; //引用本类成员

cout<<"A::x="<<A::x<<endl; //引用同名基成员

cout<<"a="<<a<<endl; //引用非同名基成员

cout<<endl;}};classC:publicB{public:

intx;voidfun(inti) { x=i*a*b; //引用非同名基类变量

cout<<“C::x=“<<x<<endl; //引用本类成员

cout<<“A::x=“<<A::x<<“,B::x=“<<B::x<<endl;//引用同名基类成员

cout<<“a=“<<a<<“,b=“<<b<<endl; //引用非同名基类成员

cout<<endl;}};

测试程序

voidmain(){

inti=2;Cobj;

obj.a=1; //引用非同名基成员

obj.A::fun(i); //引用同名基成员

obj.b=2; //引用非同名基成员

obj.B::fun(i); //引用同名基成员

obj.fun(i); //引用派生类成员

}例5.1.9classBase{public:staticvoidsb(); //静态成员

voidfb();};classD:privateBase{}; //全私有派生classDD:publicD{voidfdd();};

voidDD::fdd(){Base::sb();//对

//fb();错

//sb();错

}类层次中的类转换单基派生的情形多继承的情形含有公共虚基类的类层次结构单基派生的情形

#include<iostream.h>#include<string.h>classPerson{ char*mName; charmSex;

int

mAge;public: Person(char*n="",inta=50,chars='m') { mName=newchar[strlen(n)+1];

strcpy(mName,n);

mAge=a;

mSex=s; } ~Person() { delete[]mName; } void show() { cout<<“\nname:”<<mName;

cout<<“-age:”<<mAge;

cout<<“-sex:”<<mSex<<endl; } Person&operator=(Person&p) { delete[]mName;

mName=newchar[strlen(p.mName)+1];

strcpy(mName,p.mName);

mAge=p.mAge;

mSex=p.mSex; return*this; }};classEmployee:publicPerson{char*mDepartment;floatmSalary;public:Employee(char*,int,char,char*,float);voidshow();~Employee() {

delete[]mDepartment; }};Employee::Employee(char*nm="",int

ag=0,charsx='', char*dprt="",floatslr=0)

:Person(nm,ag,sx)

{

mDepartment=newchar[strlen(dprt)+1];

strcpy(mDepartment,dprt);

mSalary=sly;}

voidEmployee::show(){Person::print();

cout<<"-department:"<<department;

cout<<"-salary:"<<salary<<endl;}测试程序

voidmain(){Employeezh("Zhang",50,'m',"SHXIEMU",180);Personc,*pP;c=zh; //1用派生类对象给基类对象赋值

c.show();Person&y=zh; //2用派生类对象初始化基类的引用

y.show();

pP=&zh; //3把派生类对象地址赋值给基类指针

pP->show();((Employee*)pP)->show();//4基类指针向派生类指针强制转换

}多继承的情形classbase0{protected:

intb0;};classbase1:publicbase0{protected:

intb1;};classbase2:publicbase0{protected:

intb2;};classderived:publicbase1,publicbase2{floatd;public:derived();};各层次数据成员在内存中的分布Pb1intb0intb1intb0intb2…base0base0base1base2derivedPb2base0base1base2derived克服对基类成员访问的二义性对指针要显式地指明全路径将指针先强制转换到不会产生二义性的基类显式指明成员来自哪个类含有公共虚基类的类层次结构使用虚基类,在它的几条派生路经的汇合处,只产生其一个拷贝引用的都是同一个虚基类中的数据成员具有相同的值。可以进行如下的类变换

1)派生类对象的地址可以直接赋给间接公基类的指针,并且不需进行强制类型转换

2)一个虚基类的引用,可以引用一个派生类的对象类层次中成员函数名的多态性虚函数与动态绑定虚函数的访问纯虚函数与抽象类虚释放函数多基派生中虚函数的二义性虚函数与动态绑定虚函数是用关键字virtual修饰的某基类中的protected或public成员函数。它可以在派生类中重定义,以形成不同的版本。只有在程序执行过程中,依据指针具体指向哪个类对象,或依据引用哪个类对象,才能确定激活哪一个版本,实现动态绑定。即这种绑定方式不是在编译时静态地进行,而必须在程序运行过程中进行动态绑定C++中静态多态性与动态多态性的主要特征形

式绑定方式各版本的函数原型绑定依据特点函数名重载静态绑定不同参数数目及类型高效虚

数动态绑定相同程序运行时引用或指针指向高灵活性、抽象性和可扩充性点─圆类层次结构不使用虚函数

#include<iostream.h>classPoint //基类{

private:

floatmX,mY;

public:

Point(){}

Point(float

i,floatj)

{

mX=i;mY=j;

}

floatarea() //非虚函数

{

return0.0;

}

};constfloatPi=3.141593;classCircle:publicPoint //派生类{

private:

floatmRadius;

public:

Circle(floatr)

{

mRadius=r;

}

floatarea()

{

returnPi*mRadius*mRadius;

}

};点─圆类层次结构使用虚函数#include<iostream.h>

classPoint{

private:

floatmX,mY;

public:

Point(){}

Point(floati,floatj)

{

mX=i;mY=j;

}

virtualfloatarea() //声明为虚函数

{

return0.0;

}

};constfloatPi=3.141593;classCircle:publicPoint{

private:

floatmRadius;

public:

Circle(floatr)

{

mRadius=r;

}

floatarea() //虚函数的再定义

{returnPi*mRadius*mRadius;

}

};虚函数声明与重定义的一般规则1)在基类中,用关键字virtual可以将其public或protected部分的成员函数声明为虚函数2)一个虚函数是属于它所在的类层次结构的,而不是只属于某一个类,只不过它在该类层次结构中的不同类中具有不同的形态3)如果派生类中没有对基类中说明的虚函数进行重定义,则它继承基类中的虚函数4)由于有些编译器不能正确处理虚函数,因此要避免把构造函数声明为虚函数5)虚函数只能是类的成员函数,不能把虚函数声明为静态的或全局的;也不能把友元说明为虚函数。但是虚函数可以是另一个类的友元函数虚函数的访问用基指针访问与用对象名访问访问该类层次中的虚函数——要使用this指针用构造函数/释放函数访问用基指针访问与用对象名访问把一个函数声明为虚函数,保证了这个函数被指向基类的指针(或引用)调用时,C++系统对其进行动态绑定,向实际的对象传递消息但是,通过一个对象名访问虚函数时,C++系统将使用静态绑定例5.2.2

intmain()

{

Circlec(6.4321);

Cout<<c.area()<<endl;

Cout<<c.Point::area()<<endl;

Cout<<c.Circle::area()<<endl;

}这时,可以用作用域运算符指定使用的是哪个类的虚函数。这与非虚函数没有区别。这里所说的“指向基类”是指指向最先将一个成员函数声明为虚函数的类。例5.2.3

#include<iostream.h>classbase0{public:voidv(){cout<<"base0\n";} //非虚函数

};classbase1:publicbase0{public:virtualvoid

v(){cout<<"base1\n";} //声明虚函数};classA1:publicbase1{public:voidv(){cout<<"A1\n";}};classA2:publicA1{public:voidv(){cout<<"A2\n";}};classB1:privatebase1{public:voidv(){cout<<"B1\n";}};classB2:publicB1{public:voidv(){cout<<"B2\n";}};访问该类层次中的虚函数#include<iostream.h>classA{public:virtualvoidv1(){cout<<"v1iscalledinA.\n";a1();}voida1(){cout<<"a1iscalledinA.\n";v2();}virtualvoidv2(){cout<<"v2iscalledinA.\n";}};classB:publicA{public:void

b1(){cout<<"b1iscalledinB.\n";v2();}virtualvoidv2(){cout<<"v2iscalledinB.\n";}};这里,this是一个指向基类A的指针。由于v2()是一个虚函数,因此C++系统要通过动态绑定实现,具体取决于所引用的对象用构造函数/释放函数访问#include<iostream.h>classbase{public:base(){};virtualvoid

温馨提示

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

评论

0/150

提交评论