文档库 最新最全的文档下载
当前位置:文档库 › 第八章 继承与多态习题(C++)

第八章 继承与多态习题(C++)

第八章   继承与多态习题(C++)
第八章   继承与多态习题(C++)

第八章继承与多态习题

一.基本概念与基础知识自测题

8.1填空题

8.1.1 如果类α继承了类β,则类α称为(1)类,而类β称为(2)类。(3)类

的对象可作为(4)类的对象处理,反过来不行,因为(5)。如果强制转换则要注意(6)。

答案:(1)基类

(2)派生类

(3)派生类

(4)基类

(5)派生类有一些新成员

(6)只能派生类强制转换为基类

8.1.2 当用public继承从基类派生一个类时,基类的public成员成为派生类的(1)成员,

protected成员成为派生类的(2)成员,对private成员是(3)。公有派生可以使其类的(4),所以公有派生是主流。

答案:(1)public成员

(2)protected成员

(3)不可访问

(4)接口不变

8.1.3 利用继承能够实现(1)。这种实现缩短了程序开发的时间,VC++中的(2)很

好地体现了这一点。

答案:(1)代码的复用

(2)MFC编程

8.1.4 一个派生类只有一个直接基类的情况称为(1),而有多个直接基类的情况称为

(2)。继承体现了类的(3)概念,这在MFC中得到了很好表现,MFC中只采用了(4)。

答案:(1)单继承

(2)多重继承

(3)层次

(4)单继承

8.1.5 C++中多态性包括两种多态性:(1)和(2)。前者是通过(3)实现的,

而后者是通过(4)和(5)来实现的。

答案:(1)编译时的

(2)运行时的

(3)函数和运算符的重载

(4)类继承关系

(5)虚函数

8.1.6 在基类中将一个成员函数说明成虚函数后,在其派生类中只要(1)、(2)和

(3)完全一样就认为是虚函数,而不必再加关键字(4)。如有任何不同,则认为是(5)而不是虚函数。除了非成员函数不能作为虚函数外,(6)、(7)和(8)也不能作为虚函数。

答案:(1)同虚函数名

(2)同参数表

(3)同返回类型。如基类中返回基类指针,而派生类中返回派生类指针是允许的

(4)virtual

(5)重载

(6)静态成员函数

(7)内联函数

(8)构造函数

8.1.7 纯虚函数定义时在函数参数表后加(1),它表明程序员对函数(2),其本质

是将指向函数体的指针定为(3)。

答案:(1)=0

(2)不定义

(3)NULL

8.2简答题

8.2.1构造函数和析构函数可以继承吗?派生类构造函数各部分的执行次序是怎样的?答:构造函数和析构函数不可以继承。派生类构造函数各部分的执行次序是:

1.调用基类构造函数,按它们在派生类声明的先后顺序,依次调用。

2.调用新增成员对象的构造函数,按它们在类定义中声明的先后顺序,依次调用。

3.派生类的构造函数体中的操作。

8.2.2什么叫派生类的同名覆盖(override)?

答:如果派生类声明了一个和某个基类成员同名的新成员(当然如是成员函数,参数表也必须一样,否则是重载),派生类中的新成员就屏蔽了基类同名成员,类似函数中的局部变量屏蔽全局变量。称为同名覆盖(override)。

8.2.3派生类的析构函数中需完成什么任务?是否要编写对基数和成员对象的析构函数的

调用?为什么?

答:析构函数的功能是作善后工作,析构函数无返回类型也没有参数,情况比较简单。派生类析构函数定义格式与非派生类无任何差异,不要编写对基数和成员对象的析构函数的调用,只要在函数体内把派生类新增一般成员处理好就可以了,因为对新增的成员对象和基类的善后工作,系统会自己调用成员对象和基类的析构函数来完成。

8.2.4为什么要使用虚基类?怎样定义虚基类?用一个实例来解释虚基类在其派生类中的

存储方式。

答:在多重继承是有可能出现同一基类的两个拷贝,为避免这种情况,可使用虚基类。虚基类(virtual base class)定义方式如下:

class派生类名:virtual 访问限定符基类类名{...};

class派生类名:访问限定符virtual基类类名{...};

virtual 关键字只对紧随其后的基类名起作用。

如下派生:

(a)派生关系(b)存储图

存储关系如(b),在职研究生类有两个Person拷贝。采用虚基类后存储关系如下:

采用虚基类后在职研究生类储存图

8.2.5简单叙述派生类与基类的赋值兼容规则。

答:凡是基类所能解决的问题,公有派生类都可以解决。在任何需要基类对象的地方都可以用公有派生类的对象来代替,这条规则称赋值兼容规则。它包括以下情况:

1.派生类的对象可以赋值给基类的对象,这时是把派生类对象中从对应基类中继承来的成员赋值给基类对象。反过来不行,因为派生类的新成员无值可赋。

2.可以将一个派生类的对象的地址赋给其基类的指针变量,但只能通过这个指针访问派生类中由基类继承来的成员,不能访问派生类中的新成员。同样也不能反过来做。3.派生类对象可以初始化基类的引用。引用是别名,但这个别名只能包含派生类对象中的

由基类继承来的成员。

8.2.6在类中定义对象成员称为复合或嵌套,请对比复合与继承的异同之处。

答:成员对象是嵌套的概念,使用成员对象中的成员,只能直接访问(对象名加点号加成员名)公有成员。在类的成员函数中不能直接访问和处理成员对象的私有和保护成员,而要通过成员对象的接口去间接访问和处理。某些应用中,对象成员可以代替继承中的基类。

基类在派生类中只能继承一个(间接基类不在讨论之中)不能同时安排两个,否则成员名即使使用域分辨符也会发生冲突,但如果一定要用两个,只能采用成员对象。所以采用成员对象更加灵活。两者不是对立的,而是互为补充的。

8.2.7比较派生与模板各自的优点。

答:模板追求的是运行效率,而派生追求的是编程的效率。

通用性是模板库的设计出发点之一,这是由泛型算法和函数对象等手段达到的。为了运行的效率,类模板是相互独立的,即独立设计,没有使用继承的思想。对类模板的扩展是采用适配子(adapter)来完成的。应该说派生类的目标之一也是代码的复用和程序的通用性,最典型的就是MFC,派生类的优点是可以由简到繁,逐步深入,程序编制过程中可以充分利用前面的工作,一步步完成一个复杂的任务。

8.2.8是否使用了虚函数就能实现运行时的多态性?怎样才能实现运行时的多态性?

答:不是。实现动态多态性时,必须使用基类类型的指针变量或引用,使该指针指向该基类的不同派生类的对象,并通过该指针指向虚函数,才能实现动态的多态性。

8.2.9为什么析构函数总是要求说明为虚函数?

答:在基类中及其派生类中都动态分配内存空间时,必须把析构函数定义为虚函数,实现撤消对象时的多态性。根据赋值兼容规则可以用基类的指针指向派生类对象,如果由该指针撤销派生类对象,则必须将析构函数说明为虚函数,实现多态性,自动调用派生类析构函数。我们总是要求将类设计成通用的,无论其他程序员怎样调用都必须保证不出错,所以必须把析构函数定义为虚函数。

8.2.10什么是抽象类?含有纯虚函数的类是抽象类吗?

答:若定义一个类,它只能用作基类来派生出新的类,而不能用来定义对象,则称为抽象类。含有纯虚函数的类是抽象类。

8.2.11能否不提供源代码,做到用户自行把通用的软件转化为专用软件?怎样实现?

答:能不提供源代码,做到用户自行把通用的软件转化为专用软件。动态联编不一定要源代码,可以只有头文件和对象文件的.obj文件。软件开发商可在不透露其秘密的情况下发行.obj 形式的软件,然后由程序员利用继承机制,从所购得的类中派生出新类。能与软件开发商提供的类一起运行的软件也能与派生类一起运行,并能通过动态联编使用这些派生类中重定义的虚函数。比如通用的财务软件可以转化为某公司的专用软件。

二.编程与综合练习题

8.3请用类的派生方式来组织下列动物实体与概念:动物,脊椎动物亚门,节肢动物门,

鱼纲,鸟纲,爬行纲,哺乳纲,昆虫纲,鲨鱼,青鱼,海马,鹦鹉,海鸥,喜鹊,蝙蝠,翼龙,蜻蜓,金龟,扬子鳄,袋鼠,金丝猴,虎,蜈蚣,蜘蛛,蝗虫,知了,螃蟹,虾。

解:动物派生出:脊椎动物亚门和节肢动物门。

脊椎动物亚门派生出:鱼纲,鸟纲,爬行纲,哺乳纲。

鱼纲派生出:鲨鱼,青鱼,海马。

鸟纲派生出:鹦鹉,海鸥,喜鹊。

爬行纲派生出:翼龙,金龟,扬子鳄。

哺乳纲派生出:蝙蝠,袋鼠,金丝猴,虎。

节肢动物门派生出:昆虫纲,蜈蚣(多足纲),蜘蛛(蜘形纲),螃蟹,虾(甲壳纲)。

昆虫纲派生出:蜻蜓,蝗虫,知了。

8.4定义商品类及其多层的派生类。以商品类为基类。第一层派生出服装类、家电类、车

辆类。第二层派生出衬衣类、外衣类、帽子类、鞋子类;空调类、电视类、音响类;

自行车类、轿车类、摩托车类。要求给出基本属性和派生过程中增加的属性。

解:按题意没有操作,所以只列出数据成员,也不再检验

#include

using namespace std;

class Commodity{

double price; //价格

char name[20];//商品名

char manufacturer[20];//生产厂家

int items;//数量

};

class Clothing:public Commodity{//服装类

char texture[20];//材料质地

};

class Electric_Appliance:public Commodity{//家电类

enum {Black,White}type;//黑白家电

};

class Vehicle:public Commodity{//车辆类

int wheel_num;//车轮数量

};

class Shirt:public Clothing{//衬衣类

enum {Formal,Casual}Style;//式样:正式、休闲

};

class Garment:public Clothing{//外衣类

enum {Jacket,Coat}Style;//式样:夹克、外套

};

class Hat:public Clothing{//帽子类;

enum {Winter,Summer,Spring_Autumn}Style;//季节风格

};

class Shoes:public Clothing{//鞋子类

enum {Winter,Summer,Spring_Autumn}Style;//季节风格

};

class Air_Cindition:public Electric_Appliance{//空调

bool warm_cool; //是否冷暖

float power;//功率

};

class Television:public Electric_Appliance{//电视类

int Size; //尺寸

bool isColor;//是否彩色

};

class Acoustics:public Electric_Appliance{//音响类

int speaker_num; //喇叭数目

float power; //功率

};

class Bicycle:public Vehicle{//自行车类

int speed_grades; //调速级数

int wheel_size; //轮子大小

};

class Car:public Vehicle{//轿车类

float volume; //排气量

bool isSkylight; //是否有天窗

int box_num; //厢数

};

class Motorcycle:public Vehicle{//摩托车类

float volume; //排气量

};

int main(){

return 0;

}

8.5以点(point)类为基类,重新定义矩形类和圆类。点为直角坐标点,矩形水平放置,

由左下方的顶点和长宽定义。圆由圆心和半径定义。派生类操作判断任一坐标点是在图形内,还是在图形的边缘上,还是在图形外。缺省初始化图形退化为点。要求包括拷贝构造函数。编程测试类设计是否正确。

解:

#include

#include

using namespace std;

const double PI=3.1415926535;

class Point{

private:

double x,y;

public:

Point(){x = 0; y = 0; }

Point(double xv,double yv){x = xv;y = yv;}

Point(Point& pt){ x = pt.x; y = pt.y; }

double getx(){return x;}

double gety(){return y;}

double Area(){return 0;}

void Show(){cout<<"x="<

p1.Show();

cout<<"矩形rt3:"<<'\t';

rt3.Show();

switch(rt3.position(p1)){

case 0:cout<<"在矩形上"<

case -1:cout<<"在矩形内"<

case 1:cout<<"在矩形外"<

}

cout<<"圆cc3:"<<'\t';

cc3.Show();

switch(cc3.position(p1)){

case 0:cout<<"在圆上"<

case -1:cout<<"在圆内"<

case 1:cout<<"在圆外"<

}

cout<<"点p2:";

p2.Show();

cout<<"矩形rt3:"<<'\t';

rt3.Show();

switch(rt3.position(p2)){

case 0:cout<<"在矩形上"<

case -1:cout<<"在矩形内"<

case 1:cout<<"在矩形外"<

}

cout<<"圆cc3:"<<'\t';

cc3.Show();

switch(cc3.position(p2)){

case 0:cout<<"在圆上"<

}

cout<<"点p3:";

p3.Show();

cout<<"矩形rt3:"<<'\t';

rt3.Show();

switch(rt3.position(p3)){

case 0:cout<<"在矩形上"<

cout<<"圆cc3:"<<'\t';

cc3.Show();

switch(cc3.position(p3)){

case 0:cout<<"在圆上"<

}

cout<<"点p4:";

p4.Show();

cout<<"矩形rt3:"<<'\t';

rt3.Show();

switch(rt3.position(p4)){

case 0:cout<<"在矩形上"<

cout<<"圆cc3:"<<'\t';

cc3.Show();

switch(cc3.position(p4)){

case 0:cout<<"在圆上"<

}

cout<<"点p5:";

p5.Show();

cout<<"矩形rt3:"<<'\t';

rt3.Show();

switch(rt3.position(p5)){

case 0:cout<<"在矩形上"<

cout<<"圆cc3:"<<'\t';

cc3.Show();

switch(cc3.position(p5)){

case 0:cout<<"在圆上"<

case 1:cout<<"在圆外"<

}

return 0;

}

8.6几何形体的派生关系如下:

几何图形 geometric_shape)

矩形 rectangle 圆 circle 三角形(triangle)

三棱锥(t_pyramid)

长方体 box)圆柱 cylinder)圆锥 cone)三棱柱(t_prism)

对平面形体有长和面积,对立体有表面积和体积,对几何图形基类,周长、面积和体积应怎样计算(用什么函数)?对平面图形体积怎样计算(用什么函数)?对立体图形周长怎么计算(用什么函数)?要求实现运行时的多态性。请编程,并测试。

解:运行时的多态性要用指针

#include

#include

using namespace std;

const double PI=3.1415926535;

class Geometric_shape{//几何图形

public:

virtual double perimeter()=0; //周长

virtual double area()=0; //面积

virtual double volume()=0; //体积

virtual void Show(){};

};

class Circle :public Geometric_shape{//圆

double radius;

public:

Circle(){radius = 0; }

Circle(double vv){radius = vv;}

double perimeter(){return 2.0*PI*radius;} //周长

double area(){return PI*radius*radius;} //面积

double volume(){return 0;} //体积

void Show(){cout<<"radius="<

};

class Rectangle:public Geometric_shape{//矩形

double width,length;

public:

Rectangle(){width=0; length=0;}//长宽

Rectangle(double wid,double len){

width = wid;

length= len;

}

Rectangle(Rectangle& rr){

width = rr.width;

length = rr.length;

}

double perimeter(){return 2.0*(width+length);} //周长

double area(){return width*length;} //面积

double volume(){return 0;} //体积

void Show(){cout<<"width="<

};

class Triangle:public Geometric_shape{//三角形

double a,b,c;

public:

Triangle(){a=0;b=0;c=0;}

Triangle(double v1,double v2,double v3){a = v1;b = v2;c = v3;}

double perimeter(){return a+b+c;} //周长

double area(){

double s=(a+b+c)/2.0;

return sqrt(s*(s-a)*(s-b)*(s-c));

} //面积

double volume(){return 0;} //体积

void Show(){cout<<"a="<

};

class Box:public Rectangle{//长方体

double height;

public:

Box(){height=0;}

Box(double wid,double len,double heg):Rectangle(wid,len){height=heg;}

double volume(){return area()*height;} //体积

};

class Cylinder:public Circle {//圆柱体

double height;

public:

Cylinder(){height=0;}

Cylinder(double vv,double heg):Circle(vv){height=heg;}

double volume(){return area()*height;} //体积

};

class Cone: public Circle {//圆锥

double height;

public:

Cone(){height=0;}

Cone(double vv,double heg):Circle(vv){height=heg;}

double volume(){return area()*height/3;} //体积

};

class T_pyramid:public Triangle{//三棱锥

double height;

public:

T_pyramid(){height=0;}

T_pyramid(double v1,double v2,double v3,double heg):Triangle(v1,v2,v3){height=heg;} double volume(){return area()*height/3;} //体积

};

class T_prism:public Triangle{//三棱柱

double height;

public:

T_prism(){height=0;}

T_prism(double v1,double v2,double v3,double heg):Triangle(v1,v2,v3){height=heg;} double volume(){return area()*height;} //体积

};

int main(){

Geometric_shape * gs;

Circle cc1(10);

Rectangle rt1(6,8);

Triangle tg1(3,4,5);

Box bx1(6,8,3);

Cylinder cl1(10,3);

Cone cn1(10,3);

T_pyramid tpy1(3,4,5,3);

T_prism tpr1(3,4,5,3);

cc1.Show();//静态

cout<<"圆周长:"<

cout<<"圆面积:"<

cout<<"圆体积:"<

gs=&rt1;//动态

gs->Show();

cout<<"矩形周长:"<perimeter()<<'\t';

cout<<"矩形面积:"<area()<<'\t';

cout<<"矩形体积:"<volume()<

gs=&tg1;//动态

gs->Show();

cout<<"三角形周长:"<perimeter()<<'\t';

cout<<"三角形面积:"<area()<<'\t';

cout<<"三角形体积:"<volume()<

gs=&bx1;//动态

gs->Show();

cout<<"长方体底周长:"<perimeter()<<'\t';

cout<<"长方体底面积:"<area()<<'\t';

cout<<"长方体体积:"<volume()<

gs=&cl1;//动态

gs->Show();

cout<<"圆柱体底周长:"<perimeter()<<'\t';

cout<<"圆柱体底面积:"<area()<<'\t';

cout<<"圆柱体体积:"<volume()<

gs=&cn1;//动态

gs->Show();

cout<<"圆锥体底周长:"<perimeter()<<'\t';

cout<<"圆锥体底面积:"<area()<<'\t';

cout<<"圆锥体体积:"<volume()<

gs=&tpy1;//动态

gs->Show();

cout<<"三棱锥底周长:"<perimeter()<<'\t';

cout<<"三棱锥底面积:"<area()<<'\t';

cout<<"三棱锥体积:"<volume()<

gs=&tpr1;//动态

gs->Show();

cout<<"三棱柱底周长:"<perimeter()<<'\t';

cout<<"三棱柱底面积:"<area()<<'\t';

cout<<"三棱柱体积:"<volume()<

return 0;

}

8.7某公司雇员(employee)包括经理(manager),技术人员(technician)和销售员(salesman)。

开发部经理(developermanger),既是经理也是技术人员。销售部经理(salesmanager),既是经理也是销售员。

以employ类为虚基类派生出manager,technician和salesman类;再进一步派生出developermanager和salesmanager类。

employee类的属性包括姓名、职工号、工资级别,月薪(实发基本工资加业绩工资)。操作包括月薪计算函数(pay()),该函数要求输入请假天数,扣去应扣工资后,得出实发基本工资。

technician类派生的属性有每小时附加酬金和当月工作时数,及研究完成进度系数。

业绩工资为三者之积。也包括同名的pay()函数,工资总额为基本工资加业绩工资。

salesman类派生的属性有当月销售额和酬金提取百分比,业绩工资为两者之积。

也包括同名的pay()函数,工资总额为基本工资加业绩工资。

manager类派生属性有固定奖金额和业绩系数,业绩工资为两者之积。工资总额也为基本工资加业绩工资。

而developermanager类,pay()函数是将作为经理和作为技术人员业绩工资之和的一半作为业绩工资。

salesamanager类,pay()函数则是经理的固定奖金额的一半,加上部门总销售额与提成比例之积,这是业绩工资。

编程实现工资管理。特别注意pay()的定义和调用方法:先用同名覆盖,再用运行时多态。

解:

#include

#include

using namespace std;

static int Grades[]={500,600,750,1000,1400,2000,2800,4000};

class employee{

protected:

string name;//姓名

int ID;//职工号

int grade;//工资级别

double salary;//月

double base_salary;//基本月薪

double career_salary;//业绩工资

public:

employee(string="",int=0,int=0);

virtual void pay();//月薪计算函数

void show();

double getsalary(){return salary;}

double getbase_salary(){return base_salary;}

double getcareer_salary(){return career_salary;}

};

employee::employee(string nn,int id,int gr){

name=nn;

ID = id;

grade = gr;

salary=0;//月薪

base_salary=0;//基本月薪

career_salary=0;//业绩工资

}

void employee::show(){

cout<

}

void employee::pay(){

int days;

cout<<"请输入请假天数:\n";

cin>>days;

base_salary = Grades[grade]*(23 - days)/23;

career_salary = base_salary/2;//普通员工业绩工资为基本工资的一半salary = base_salary+career_salary;

}

class manager:virtual public employee{//虚基类

protected:

double prize;//固定奖金额

double factor;//业绩系数

public:

manager(string="",int=0,int=0,double=0);

void pay();

};

manager::manager(string nn,int id,int gr,double pr):employee(nn,id,gr){ prize = pr;//固定奖金额

factor = 0;

}

void manager::pay(){

int days;

cout<<"请输入请假天数:\n";

cin>>days;

cout<<"请输入业绩系数:\n";

cin>>factor;

base_salary = Grades[grade]*(23 - days)/23;

career_salary = prize*factor*(23 - days)/23;

salary = base_salary + career_salary ;

}

class technician:virtual public employee{

protected:

double hours;//月工作时数

double perhour;//每小时附加酬金

double shfactor;//研究进度系数

public:

technician(string="",int=0,int=0,double=0);

void pay();

};

technician::technician(string nn,int id,int gr,double phr):employee(nn,id,gr){ hours = 0;

perhour = phr;//每小时附加酬金

shfactor = 0;

}

void technician::pay(){

int days;

cout<<"请输入请假天数:\n";

cin>>days;

cout<<"请输入研究进度系数:\n";

cin>>shfactor;

hours=8*(23-days);

base_salary = Grades[grade]*(23 - days)/23;

career_salary = perhour*hours*shfactor*(23 - days)/23;

salary= base_salary + career_salary ;

}

class salesman:virtual public employee{

protected:

double amount;//销售额

double slfactor;//提成比例

public:

salesman(string="",int=0,int=0,double=0);

void pay();

};

salesman::salesman(string nn,int id,int gr,double slfac):employee(nn,id,gr)

{

amount = 0;

slfactor = slfac;//提成比例

}

void salesman::pay(){

int days;

cout<<"请输入请假天数:\n";

cin>>days;

cout<<"请输入销售额:\n";

cin>>amount;

base_salary = Grades[grade]*(23 - days)/23;

career_salary = amount*slfactor;

salary = base_salary + career_salary ;

}

class developermanager:public manager,public technician {

public:

developermanager(string="",int id=0,int gr=0,double pr=0, double phr=0);

void pay();

};

developermanager::developermanager(string nn,int id,int gr,double pr,double phr) :manager(nn,id,gr,pr),technician(nn,id,gr,phr),employee(nn,id,gr){}

void developermanager::pay(){

int days;

cout<<"请输入请假天数:\n";

cin>>days;

cout<<"请输入业绩系数:\n";

cin>>factor;

cout<<"请输入研究进度系数:\n";

cin>>shfactor;

hours=8*(23-days);

base_salary = Grades[grade]*(23 - days)/23;//基本工资

career_salary = perhour*hours*shfactor*(23 - days)/23;//技术人员奖金

career_salary += prize*factor*(23 - days)/23;//加经理业绩工资

career_salary /=2;

salary = base_salary + career_salary ;

}

class salesmanager:public manager,public salesman{

public:

salesmanager(string=NULL,int=0,int=0,double pr=0, double slfac=0);

void pay();

};

salesmanager::salesmanager(string nn,int id,int gr,double pr,double slfac) :manager(nn,id,gr,pr),salesman(nn,id,gr,slfac),employee(nn,id,gr){}

void salesmanager::pay(){

int days;

cout<<"请输入请假天数:\n";

cin>>days;

cout<<"请输入业绩系数:\n";

cin>>factor;

cout<<"请输入销售额:\n";

cin>>amount;

base_salary = Grades[grade]*(23 - days)/23;//基本工资

career_salary = prize*factor*(23 - days)/23;//经理业绩工资career_salary /=2;

career_salary += amount*slfactor;//加销售业绩工资

salary = base_salary + career_salary ;

}

int main(){

employee eml1("张伟",10012,0),*emlp;

manager mag1("姚婕",20005,4,1000);

technician tec1("王茜",30047,5,10);

salesman sal1("朱明",40038,2,0.05);

developermanager dem1("沈俊",50069,6,1500,12);

salesmanager sam1("况钟",60007,3,1000,0.05);

eml1.pay();

eml1.show();

mag1.pay();

mag1.show();

tec1.pay();

tec1.show();

sal1.pay();

sal1.show();

emlp=&dem1;

emlp->pay();

emlp->show();

emlp=&sam1;

emlp->pay();

emlp->show();

return 0;

}

8.8为上题添加拷贝构造函数,并测试是否正确。

解:

#include

#include

using namespace std;

static int Grades[]={500,600,750,1000,1400,2000,2800,4000}; class employee{

protected:

string name;//姓名

int ID;//职工号

int grade;//工资级别

double salary;//月

double base_salary;//基本月薪

double career_salary;//业绩工资

public:

employee(string="",int=0,int=0);

employee(employee &); //拷贝构造函数

virtual void pay(); //月薪计算函数

void show();

double getsalary(){return salary;}

double getbase_salary(){return base_salary;}

double getcareer_salary(){return career_salary;}

};

employee::employee(string nn,int id,int gr){

name=nn;

ID = id;

grade = gr;

salary=0; //月薪

base_salary=0; //基本月薪

career_salary=0; //业绩工资

}

employee::employee(employee &emp){ //拷贝构造函数

name=https://www.wendangku.net/doc/e17921797.html,;

ID=emp.ID;

grade =emp.grade;

salary=emp.salary; //月薪

base_salary=emp.base_salary; //基本月薪

career_salary=emp.career_salary;//业绩工资

}

void employee::show(){

cout<

}

void employee::pay(){

int days;

cout<<"请输入请假天数:\n";

cin>>days;

base_salary = Grades[grade]*(23 - days)/23;

career_salary = base_salary/2; //普通员工业绩工资为基本工资的一半

salary = base_salary+career_salary;

}

class manager:virtual public employee{//虚基类

protected:

double prize;//固定奖金额

double factor;//业绩系数

public:

manager(string="",int=0,int=0,double=0);

manager(manager&mag); //拷贝构造函数

void pay();

};

manager::manager(manager&mag):employee(mag){//按赋值兼容规则mag可为employee的实参

prize=mag.prize;

factor=mag.factor;

}

manager::manager(string nn,int id,int gr,double pr):employee(nn,id,gr){

prize = pr;//固定奖金额

factor = 0;

}

void manager::pay(){

int days;

cout<<"请输入请假天数:\n";

cin>>days;

cout<<"请输入业绩系数:\n";

cin>>factor;

base_salary = Grades[grade]*(23 - days)/23;

career_salary = prize*factor*(23 - days)/23;

salary = base_salary + career_salary ;

}

class technician:virtual public employee{

protected:

double hours;//月工作时数

double perhour;//每小时附加酬金

double shfactor;//研究进度系数

public:

technician(string="",int=0,int=0,double=0);

technician(technician &); //拷贝构造函数

void pay();

};

technician::technician(technician& tech):employee(tech){ //拷贝构造函数hours=tech.hours;//月工作时数

perhour=tech.perhour;//每小时附加酬金

shfactor=tech.shfactor;//研究进度系数

}

technician::technician(string nn,int id,int gr,double phr):employee(nn,id,gr){ hours = 0;

perhour = phr;//每小时附加酬金

shfactor = 0;

}

void technician::pay(){

int days;

cout<<"请输入请假天数:\n";

cin>>days;

cout<<"请输入研究进度系数:\n";

hours=8*(23 - days);

cin>>shfactor;

base_salary = Grades[grade]*(23 - days)/23;

career_salary = perhour*hours*shfactor*(23 - days)/23;

salary= base_salary + career_salary ;

}

class salesman:virtual public employee{

protected:

double amount;//销售额

double slfactor;//提成比例

public:

salesman(string="",int=0,int=0,double=0);

salesman(salesman &); //拷贝构造函数

void pay();

};

salesman::salesman(string nn,int id,int gr,double slfac):employee(nn,id,gr){ amount = 0;

slfactor = slfac;//提成比例

}

salesman::salesman(salesman &sale):employee(sale){ //拷贝构造函数amount=sale.amount;//销售额

slfactor=sale.slfactor;//提成比例

}

void salesman::pay(){

int days;

cout<<"请输入请假天数:\n";

cin>>days;

cout<<"请输入销售额:\n";

cin>>amount;

base_salary = Grades[grade]*(23 - days)/23;

career_salary = amount*slfactor;

salary = base_salary + career_salary ;

}

class developermanager:public manager,public technician {

public:

developermanager(string="",int id=0,int gr=0,double pr=0, double phr=0);

developermanager(developermanager &); //拷贝构造函数

void pay();

};

developermanager::developermanager(string nn,int id,int gr,double pr,double phr) :manager(nn,id,gr,pr),technician(nn,id,gr,phr),employee(nn,id,gr){} developermanager::developermanager(developermanager &deman)

:manager(deman),technician(deman),employee(deman){} //拷贝构造函数void developermanager::pay(){

int days;

cout<<"请输入请假天数:\n";

cin>>days;

cout<<"请输入业绩系数:\n";

cin>>factor;

cout<<"请输入研究进度系数:\n";

cin>>shfactor;

hours=8*(23 - days);

base_salary = Grades[grade]*(23 - days)/23;//基本工资

career_salary = perhour*hours*shfactor*(23 - days)/23;//技术人员奖金

career_salary += prize*factor*(23 - days)/23;//加经理业绩工资

career_salary /=2;

salary = base_salary + career_salary ;

}

class salesmanager:public manager,public salesman{

public:

salesmanager(string="",int=0,int=0,double pr=0, double slfac=0);

salesmanager(salesmanager &); //拷贝构造函数

void pay();

};

salesmanager::salesmanager(string nn,int id,int gr,double pr,double slfac)

:manager(nn,id,gr,pr),salesman(nn,id,gr,slfac),employee(nn,id,gr){} salesmanager::salesmanager(salesmanager& salman)

:manager(salman),salesman(salman),employee(salman){} //拷贝构造函数void salesmanager::pay(){

int days;

cout<<"请输入请假天数:\n";

cin>>days;

cout<<"请输入业绩系数:\n";

cin>>factor;

cout<<"请输入销售额:\n";

cin>>amount;

base_salary = Grades[grade]*(23 - days)/23;//基本工资

career_salary = prize*factor*(23 - days)/23;//经理业绩工资

career_salary /=2;

career_salary += amount*slfactor;//加销售业绩工资

salary = base_salary + career_salary ;

}

int main(){

employee eml1("张伟",10012,0),*emlp;

manager mag1("姚婕",20005,4,1000);

technician tec1("王茜",30047,5,10);

salesman sal1("朱明",40038,2,0.05);

developermanager dem1("沈俊",50069,6,1500,12);

salesmanager sam1("况钟",60007,3,1000,0.05);

eml1.pay();

eml1.show();

mag1.pay();

mag1.show();

tec1.pay();

tec1.show();

sal1.pay();

sal1.show();

emlp=&dem1;

emlp->pay();

emlp->show();

emlp=&sam1;

emlp->pay();

emlp->show();

cout<

employee eml0(eml1);

manager mag0(mag1);

technician tec0(tec1);

salesman sal0(sal1);

developermanager dem0(dem1);

salesmanager sam0(sam1);

cout<<"输出拷贝结果:"<

eml0.show();

mag0.show();

tec0.show();

sal0.show();

emlp=&dem0;

emlp->show();

emlp=&sam0;

emlp->show();

return 0;

}

8.9采用纯虚函数实现多态性来建立通用的双向链表派生类。参考【例8.10】和【例7.7】。解:

//ep8_9.h

#include

using namespace std;

//首先看结点组织,采用结点类加数据类

class Object{//数据类为抽象类

public:

Object(){}

virtual bool operator>(Object &)=0;//纯虚函数,参数必须为引用或指针

virtual bool operator!=(Object &)=0;//纯虚函数,参数必须为引用或指针

virtual void Print()=0;//纯虚函数

virtual ~Object(){} //析构函数可为虚函数,构造函数不行

};

class DblNode{

Object* info; //数据域用指针指向数据类对象

DblNode *llink,*rlink; //前驱(左链)、后继(右链)指针

public:

DblNode(); //生成头结点的构造函数

~DblNode();

void Linkinfo(Object* obj);

friend class DblList;

//以DblList为友元类,DblList可直接访问DblNode的私有函数,与结构一样方便,但更安全

};

DblNode::DblNode(){info=NULL;llink=rlink=NULL;}

DblNode::~DblNode(){

cout<<"删除结点类"<<'\t';

delete info; //释放数据域

}

void DblNode::Linkinfo(Object * obj){info=obj;}

//再定义双链表类,选择常用操作

class DblList{

DblNode *head,*current;

public:

DblList();//构造函数,生成头结点(空链表)

~DblList();//析构函数

void MakeEmpty();//清空链表,只余表头结点

void InsertFront(DblNode* p); //可用来向前生成链表,在表头插入一个结点

void InsertRear(DblNode* p); //可用来向后生成链表,在表尾添加一个结点

void InsertOrder(DblNode* p); //按升序生成链表

DblNode* CreatNode();//创建一个结点(孤立结点)

DblNode* DeleteNode(DblNode* p); //删除指定结点

void PrintList();//打印链表的数据域

int Length();//计算链表长度

DblNode *Find(Object & obj);//搜索数据域与定值相同的结点,返回该结点的地址

//其它操作

};

DblList::DblList(){//建立表头结点

head=new DblNode();

head->rlink=head->llink=head;

current=NULL;

}

DblList::~DblList(){

MakeEmpty();//清空链表

cout<<"删除头结点:";

delete head;

}

void DblList::MakeEmpty(){

DblNode *tempP;

while(head->rlink!=head){

tempP=head->rlink;

head->rlink=tempP->rlink;//把头结点后的第一个节点从链中脱离

tempP->rlink->llink=head;//处理左指针

delete tempP; //删除(释放)脱离下来的结点

}

current=NULL; //current指针恢复

}

void DblList::InsertFront(DblNode *p){

p->llink=head;//注意次序

p->rlink=head->rlink;

head->rlink->llink=p;

继承与多态习题

继承与多态习题 一.基本概念与基础知识自测题 8.1填空题 8.1.1 如果类α继承了类β,则类α称为(1)类,而类β称为(2)类。(3)类 的对象可作为(4)类的对象处理,反过来不行,因为(5)。如果强制转换则要注意(6)。 答案:(1)基类 (2)派生类 (3)派生类 (4)基类 (5)派生类有一些新成员 (6)只能派生类强制转换为基类 8.1.2 当用public继承从基类派生一个类时,基类的public成员成为派生类的(1)成员, protected成员成为派生类的(2)成员,对private成员是(3)。公有派生可以使其类的(4),所以公有派生是主流。 答案:(1)public成员 (2)protected成员 (3)不可访问 (4)接口不变 8.1.3 利用继承能够实现(1)。这种实现缩短了程序开发的时间,VC++中的(2)很 好地体现了这一点。 答案:(1)代码的复用 (2)MFC编程 8.1.4 一个派生类只有一个直接基类的情况称为(1),而有多个直接基类的情况称为 (2)。继承体现了类的(3)概念,这在MFC中得到了很好表现,MFC中只采用了(4)。 答案:(1)单继承 (2)多重继承 (3)层次 (4)单继承 8.1.5 C++中多态性包括两种多态性:(1)和(2)。前者是通过(3)实现的, 而后者是通过(4)和(5)来实现的。 答案:(1)编译时的 (2)运行时的 (3)函数和运算符的重载 (4)类继承关系 (5)虚函数 8.1.6 在基类中将一个成员函数说明成虚函数后,在其派生类中只要(1)、(2)和

(3)完全一样就认为是虚函数,而不必再加关键字(4)。如有任何不同,则认为是(5)而不是虚函数。除了非成员函数不能作为虚函数外,(6)、(7)和(8)也不能作为虚函数。 答案:(1)同虚函数名 (2)同参数表 (3)同返回类型。如基类中返回基类指针,而派生类中返回派生类指针是允许的 (4)virtual (5)重载 (6)静态成员函数 (7)内联函数 (8)构造函数 8.1.7 纯虚函数定义时在函数参数表后加(1),它表明程序员对函数(2),其本质 是将指向函数体的指针定为(3)。 答案:(1)=0 (2)不定义 (3)NULL 8.2简答题 8.2.1构造函数和析构函数可以继承吗?派生类构造函数各部分的执行次序是怎样的?答:构造函数和析构函数不可以继承。派生类构造函数各部分的执行次序是: 1.调用基类构造函数,按它们在派生类声明的先后顺序,依次调用。 2.调用新增成员对象的构造函数,按它们在类定义中声明的先后顺序,依次调用。 3.派生类的构造函数体中的操作。 8.2.2什么叫派生类的同名覆盖(override)? 答:如果派生类声明了一个和某个基类成员同名的新成员(当然如是成员函数,参数表也必须一样,否则是重载),派生类中的新成员就屏蔽了基类同名成员,类似函数中的局部变量屏蔽全局变量。称为同名覆盖(override)。 8.2.3派生类的析构函数中需完成什么任务?是否要编写对基数和成员对象的析构函数的 调用?为什么? 答:析构函数的功能是作善后工作,析构函数无返回类型也没有参数,情况比较简单。派生类析构函数定义格式与非派生类无任何差异,不要编写对基数和成员对象的析构函数的调用,只要在函数体内把派生类新增一般成员处理好就可以了,因为对新增的成员对象和基类的善后工作,系统会自己调用成员对象和基类的析构函数来完成。 8.2.4为什么要使用虚基类?怎样定义虚基类?用一个实例来解释虚基类在其派生类中的 存储方式。 答:在多重继承是有可能出现同一基类的两个拷贝,为避免这种情况,可使用虚基类。虚基类(virtual base class)定义方式如下: class派生类名:virtual 访问限定符基类类名{...}; class派生类名:访问限定符virtual基类类名{...}; virtual 关键字只对紧随其后的基类名起作用。

第七章继承多态练习题

第七章继承多态 一、选择题: 1、分析: class A { A() { } } class B extends A { //系统自动生成的构造方法和类的访问权限一样 } 哪两种说法是正确的? ( ) A:类B的构造方法是public的. B:类B的构造方法包含对this()的调用. C:类B的构造方法没有参数. D:类B的构造方法包含对super()的调用. 2、运行结果是:() class Base { Base() { System.out.print("Base"); } } public class Alpha extends Base { public static void main( String[] args ) { new Alpha(); new Base(); } } A: Base B: BaseBase C: 编译失败. D: 没有输出. E: 运行时异常. 3. 程序的运行结果是?() A: 编译失败. B: hello from a C: hello from b D: hello from b E: hello from a hello from a hello from b

4. 运行结果是:() class TestSuper { TestSuper(int i) { } } class TestSub extends TestSuper{ } class TestAll { public static void main (String [] args) { new TestSub(); } } A: 编译失败. B: 程序运行没有异常. C: 第7行抛出异常. D: 第2行抛出异常. 5. 程序的运行结果是?() A: 0 B: 1 C: 2 D: 编译失败. 6. 对于语句"B is a D" 和"B has a C",一下哪两种说法是正确的? ( ) A:D是B. B:B是D. C:D是C. D:B是C. E:D继承B. F:B 继承D. 7. 运行结果是?()

JAVA面向对象基础测试题,继承,封装,多态等测试题

JAVA面向对象基础测试题 提示:本题为第一阶段,JAVA面向对象基础部分练习题,包括对象,类,继承,封装,多态,接口,内部类等等,java核心基础,适合初学者对面向对象基础的知识进行测试,以便查漏补缺。 1. 程序执行的结果是:()。 01 public class Point{ 02 int y = 7; 03 public void step(int y) { 04 y += y; 05 System.out.println(y); 06 } 07 public static void main(String[] args) { 08

Point p = new Point(); 09 p.step(10); 10 } 11 } A.14 B.20 C.10 D.17 正确答案:B解析: 2. 程序的执行结果是:()。 01 public class Question { 02 private int num; 03 public static void main(String [] args){ 04 Question q = new Question();

05 q.num=13; 06 update(q); 07 System.out.println(q.num); 08 } 09 public static void update(Question q){ 10 q.num=9; 11 } 12 } A.13 B.9 C.0 D.4 正确答案:B解析: 3.

程序执行的结果是:()。 01 public class Answer { 02 public static void main(String[] args) { 03 int score = 20; 04 Answer ans= new Answer(); 05 ans.add(score); 06 System.out.println(" main:score = " + score); 07 } 08 void add(int score) { 09 System.out.println(" add:score=" + score++); 10 } 11

java实验报告2 继承多态 封装

实验报告 学生姓名:学号:专业班级: 实验类型:□验证□综合□设计□创新实验日期:实验成绩: 一、实验项目名称抽象、封装与类(一) 二、实验目的 1.掌握抽象、封装、继承和多态 2.熟练掌握如何自定义一个类。 3.掌握类的访问权限 4.掌握类成员的访问控制

三、实验基本原理 四、主要仪器设备及耗材 Myclipse 五、实验步骤 1、调试程序例3-4 2、编写如下程序。 (1)定义一个表示学生的类Student,包括属性“学号”、“班号”、“姓名”、“性别”、“年龄”;方法“获得学号”、“获得班号”、“获得姓名”、“获得性别”、“获得年龄”、“修改年龄”。 (2)为Student类定义构造函数初始化所有属性,增加一个方法public String toString(),把Student类对象的所有属性信息组合成一个字符串输出。 (3)在main()中创建Student类对象,测试自定义类。 六、实验数据及处理结果 1、package ncu;

public class Exam3_4 { static double d; public static void main(String args[]){ int i; Exam3_4 pt=new Exam3_4(); i=5; (i); "int type value is:"+i); =; (pt); "double type value is"+d); } public void changeInt(int v) {v=10;} public void changeDouble(Exam3_4 ref) {=;} }

多态练习题

1、生成表示学生的类XS,提供成员函数dispXM()、dispXB()和dispNL()分别用来显示姓名、性别和年龄,并将他们全部定义为纯虚函数;生成CZS类表示初中生,包含数据成员xm、xb和nl表示学生的姓名、性别和年龄,提供成员函数dispXM()、dispXB()和dispNL()分别用来显示姓名、性别和年龄;再生成类GZS表示高中生和类DXS表示大学生,同样包含相同含义的数据成员xm、xb和nl,也包括成员函数dispXM()、dispXB()和dispNL()。要求: (1)设计和实现基本类XS (2)设计和实现派生类CZS,GZS和DXS (3)分别生成CZS、GZS、DXS类对象 (4)将CZS、GZS、DXS类对象的指针赋给指针赋给XS类的指针变量。 (5)分别用XS类的指针和引用访问dispXM()、dispXB()和dispNL()函数。 (6)观察程序结果。 #include #include using namespace std; class XS{ public: virtual void dispXM()=0; virtual void dispXB()=0; virtual void dispNL()=0; }; class CZS:public XS{ public: CZS(char * m="张三",int b=1,int n=14); void dispXM(); void dispXB(); void dispNL(); protected: char xm[9]; int xb,nl; }; CZS::CZS(char *m,int b,int n) { strcpy(xm,m); xb=b,nl=n; } void CZS::dispXM() { cout<<"name:"<

JAVA继承和多态实验报告

实验项目名称:继承和多态 (所属课程:Java语言程序设计) 院系:专业班级:姓名: 学号:实验地点:指导老师: 本实验项目成绩:教师签字:日期: 1.实验目的 (1)掌握类的继承机制。 (2)熟悉类中成员变量和方法的访问控制。 (3)熟悉方法或构造方法多态性。 2.实验内容 (1)模拟编写程序,理解类的继承、多态、继承和多态规则。 (2)独立编程,实现类的继承和多态。 3.实验作业 设计一个类Shape(图形)包含求面积和周长的area()方法和perimeter()方法以及设置颜色的方法SetColor(),并利用Java多态技术设计其子类Circle(圆形)类、Rectangle (矩形)类和Triangle(三角形)类,并分别实现相应的求面积和求周长的方法。每个类都要覆盖toString方法。 海伦公式:三角形的面积等于s(s-a)(s-b)(s-c)的开方,其中s=(a+b+c)/2 程序代码为: Class包 package Class; public class Shape { private String color = "while"; public Shape(String color){ this.color = color; } public void setColor(String color){ this.color = color; } public String getColor(){ return color;

} public double getArea(){ return 0; } public double getPerimeter(){ return 0; } public String toString(){ return"color:" + color; } } package Class; public class Circle extends Shape { private double radius; public Circle(String color,double radius) { super(color); this.radius = radius; } public void setRadius(double radius){ this.radius = radius; } public double getRadius(){ return radius; } public double getCircleArea(){ return 3.14*radius*radius; } public double getCirclePerimeter(){ return 3.14*2*radius; } public String toString(){

如何体现封装、继承、多态 编程人员看看

如何体现封装、继承、 编程人员看看! 如何体现封装、继承、多态 编程人员看看!! ! 什么事封装? 1. 保护数据成员, 不让类以外的程序直接访问或 修改,只能通过提供的公共的接口访问==>数据封 装。 2. 方法的细节对用户是隐藏的,只要接口不变, 内容的修改不会影响到外部的调用者==>方法封装。 3. 当对象含有完整的属性和与之对应的方法时称为 封装。 4. 从对象外面不能直接访问对象的属性, 只能通 过和该属性对应的方法访问。 5. 对象的方法可以接收对象外面的消息。 比如: Class A { private int prop; puplic int getProp() { return prop; }

public void setProp(int prop) { this.prop = prop; } } 属性 prop 是 private 的,外界不能直接访问, 但是外界可以通过调用 getProp()和 setProp()的方 法, 给对象发消息,从而完成某种功能。 什么事多态? 多态性的概念经常被说成事“一个接口,多种方 法”。这意味着可以为一组相关的动作作设计一个通 用 的接口。多态允许同一个接口被必于同一个类的多个 动作使用,这样就降低了程序的复杂性。再拿狗作比 喻, 一条狗的嗅觉是多态的。如果狗闻到猫的气味,它会 在吠叫并且追着它跑。如果狗闻到食物的气味,它将 分 泌唾液并向盛着食物的碗跑去。两种状况下同一种嗅 觉器官在工作,差别在于问到了什么气味,也就是有

两 种不同类型的数据作用于狗的鼻子!在 java 中,同一 个类中的 2 个或 2 个以上的方法可以有同一个名字, 只要 参数声明不同即可。在这种情况下,该方法就被称为 重载(Overload),这个过程称为方法重载(Method overloading)。方法重载是 java 实现多态的一种方 式。 有两种方式可以实现多态:* 1. 继承(子类继承父类(包括 abstract class,interf ace ect)) 2. 重载(同一个类中) 如果是面向对象程序设计的话,面向对象程序设 计中的另外一个重要概念是多态性。在运行时,通过 指向 基类的指针,来调用实现派生类中的方法。可以把一 组对象放到一个数组中,然后调用它们的方法,在这 种场 合下,多态性作用就体现出来了,这些对象不必是相 同类型的对象。当然它们都继承自某个类,你可以把 这些 派生类都放到一个数组中。如果这些对象都有同名方

实验三 继承、多态和图形用户界面

实验三字符串与文本 (上课被教师验收的同学不用提交实验结果,并注意在教师处签名。未完成的同学请提交实验结果,即将程序直接粘贴至本DOC文件,并在邮件名的标题中注明:学号+姓名+实验三) 1.给出下面程序的输出: 2.直接给出下面程序的运行结果: “B’s constructor is invoked” “A’s constructor is invoked” 2. 下面的说法对的注明“True”,错的注明“False” (1)子类是父类的一个子集。(f)(2)当从子类调用一个构造函数时,它的父类的无参构造方法总是被调用。(f)(3)可以覆盖定义在父类中的私有方法。( f) (4)可以覆盖定义在父类中的静态方法。( f) (5) 如果子类中的一个方法和它的父类中的方法具有完全相同的方法头,但返回值 类型不同,也可以构成覆盖。( f) (6) 可以给面板添加一个框架。( f) (7) 可以给框架添加一个面板。( t ) 3. 指出下面程序中的问题:

有四处错误 给radius和length赋值要用this. 调用父类的构造方法Circle()和getArea()前需要加上关键字super 4.下面的程序是要在框架中显示一个按钮,但是什么也没有显示出来。这个程序有什么问 题?

Test()中没有创建实例对象 将new javax.swing.JFrame() 改成new Test() 5.填空: (1)在类上使用default修饰符,才能使同一个包中的类可以访问它,而不同包中的类不能访问它。 (2)在类上使用protected 修饰符,才能使不同包中的类不能访问这个类,而任何包中的子类可以访问它。 6. 在程序清单11-9中,MyStack是用组合实现的,现请扩展ArrayList,利用继承来实现MyStack。编写一个测试程序,提示用户输入五个字符串,然后以逆序显示这些字符串。 package Test03; public class MyStack extends java.util.ArrayList { public Object peek() { return this.get(size() - 1); } public Object pop() { Object o = this.get(size() - 1); this.remove(size() - 1);

C 的封装性、继承性和多态性概念

C++的封装性、继承性和多态性概念 封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员。例如,在抽象的基础上,我们可以将时钟的数据和功能封装起来,构成一个时钟类。按c++的语法,时钟类的声明如下:class Clock { public: //共有成员,外部借口void SetTime(int NewH,int NewM,int NewS); void ShowTime(); private: //私有成员,外部无法访问int Hour,Minute,Second; } 可以看到通过封装使一部分成员充当类与外部的接口,而将其他的成员隐蔽起来,这样就达到了对成员访问权限的合理控制,使不同类之间的相互影响减少到最低限度,进而增强数据的安全性和简化程序的编写工作。什么是多态(Polymorphisn)?按字面的意思就是“多种形状”。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为和一个或更多的他的子对象相等 的技术,赋值之后,>>>父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作<<<(摘自“Delphi4 编程技术内幕”)。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在Object Pascal和C++中都是通过虚函数(Virtual Function)实现的。好,接着是“虚函数”(或者是“虚方法”)。虚函数就是允许被其子类重新定

义的成员函数。而子类重新定义父类虚函数的做法,称为“覆盖”(override),或者称为“重写”。“继承”是面向对象软件技术当中的一个概念。如果一个类A继承自另一个类B,就把这个A称为"B的子类",而把B称为"A的父类"。继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。在令子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。 ... 继承是指一个对象直接使用另一对象的属性和方法。事实上,我们遇到的很多实体都有继承的含义。例如,若把汽车看成一个实体,它可以分成多个子实体,如:卡车、公共汽车等。这些子实体都具有汽车的特性,因此,汽车是它们的"父亲",而这些子实体则是汽车的"孩子"。19. 多态的作用?主要是两个:1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用; 2. 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。

继承与多态实验报告

《高级Java编程技术》课程实验报告 院(系)名称:计算机学院专业班级:09计科 3 班 学号:姓名: 实验题目:实验10继承与多态 实验日期:2011/11/29实验(上机)学时:2 一、实验内容、要求 1.实验内容:计算课程成绩 2.要求: 编写应用程序,读入以下风格的文本文件,并显示最后的课程成绩,根据列出的公式,对于本科生和研究生,课程成绩的计算方法不同。输入的文本格式如下: (1)用单独的一行表示一个学生的信息。 (2)每行使用下面的格式: 其中,指明学生是研究生,还是本科生;指明学 生的姓和名;指明是第i次测试成绩。 (3)“END”指明输入的结束。字母的大小写不重要。 公式如下: 学生类型评分方案 本科生通过,如果(test1+test2+test3)/3>=70 研究生通过,如果(test1+test2+test3)/3>=80 实现一个类完成以下功能: (1)读入输入文本文件。

(2)计算课程成绩。 (3)输出结果。 我们将用五个增量步骤开发此程序: (1)以程序结构开始,定义ComputeGrades类的框架。 (2)实现printResult方法,定义实现printResult所必需的任何其他方法。 (3)实现computerGrades方法,定义实现computerGrades所必需的任何其他方法。 (4)实现readDate方法,定义实现readDate所必需的任何其他方 法。 (5)最后完成并改进。 二、所采用的数据结构 如:线性表,二叉树,树,图等。你在此用到了啥样的数据结构就写啥。无数据结构。 三、实验的主要模块(或函数)及其功能 函数分块及各块主要功能的文字描述 ComputGrades类:这是本程序的主类,且是MainWindow的子类。MainWindows本身是Jframe的子类,并且又将其自身放在屏幕中间的功能。ComputGrades类包含的方法如下: 1、构造方法。生成任意大小的数组,让程序员在构造方法中传递数组的 大小,不在构造方法中生成数组。 2、printResult方法。将学生信息机计算出来的成绩输出到OutputBox

JAVA面向对象基础测试题-继承-封装-多态等测试题教程文件

J A V A面向对象基础测试题-继承-封装-多 态等测试题

JAVA面向对象基础测试题 提示:本题为第一阶段,JAVA面向对象基础部分练习题,包括对象,类,继承,封装,多态,接口,内部类等等,java核心基础,适合初学者对面向对象基础的知识进行测试,以便查漏补缺。 1. 程序执行的结果是:()。 01 public class Point{ 02 int y = 7; 03 public void step(int y) { 04 y += y; 05 System.out.println(y); 06 } 07 public static void main(String[] args) { 08 Point p = new Point(); 09 p.step(10); 10 } 11 } A.14 B.20 C.10 D.17 正确答案:B解析: 2. 程序的执行结果是:()。 01 public class Question { 02 private int num; 03 public static void main(String [] args){ 04 Question q = new Question();

q.num=13; 06 update(q); 07 System.out.println(q.num); 08 } 09 public static void update(Question q){ 10 q.num=9; 11 } 12 } A.13 B.9 C.0 D.4 正确答案:B解析: 3. 程序执行的结果是:()。 01 public class Answer { 02 public static void main(String[] args) { 03 int score = 20; 04 Answer ans= new Answer(); 05 ans.add(score); 06 System.out.println(" main: score = " + score); 07 } 08 void add(int score) { 09 System.out.println(" add: score=" + score++); 10 } 11

C++习题3(继承和多态)

一、选择题 1. 在C++中,类与类之间的继承关系具有(C ) A)自反性B)对称性C)传递性D)反对称性 2. 在公有继承的情况下,基类的成员(私有的除外)在派生类中的访问权限(B A)受限制B)保持不变C)受保护D)不受保护 3. 按解释中的要求在下列程序划线处填入的正确语句是: (C ) #in elude class Base{ public: void fun(){coutfun(); 4. 在保护继承的情况下,基类的成员(私有的除外)在派生类中的访问权限(C ) A)受限制B)保持不变C)受保护D)不受保护 5. 在哪种派生方式中,派生类可以访问基类中的protected 成员(B ) A)public 禾口private B)public 、protected 禾口 private C)protected 禾口private D)仅protected

6. 当一个派生类仅有protected继承一个基类时,基类中的所有公有成员成为派生类的(C) A) public 成员B) private 成员C) protected

成员D) 友元 7. 不论派生类以何种方法继承基类,都不能使用基类的(B ) A) public 成员B) private 成员C) protected 成员D) public 成员和protected 成员 8 下面叙述错误的是( S )。 A) 基类的protected 成员在派生类中仍然是protected 的 B) 基类的protected 成员在public 派生类中仍然是protected 的 C) 基类的protected 成员在private 派生类中是private 的 D) 基类的protected 成员不能被派生类的对象访问 9. 下列说法中错误的是( S )。 A) 保护继承时基类中的public 成员在派生类中仍是public 的 B) 公有继承时基类中的private 成员在派生类中仍是private 的 C) 私有继承时基类中的public 成员在派生类中是private 的 D) 保护继承时基类中的public 成员在派生类中是protected 的 10下面叙述错误的是( C)。 A) 派生类可以使用private 派生 B) 对基类成员的访问必须是无二义性的 C) 基类成员的访问能力在派生类中维持不变 D) 赋值兼容规则也适用于多继承的组合 11派生类的构造函数的成员初始化列表中,不能包含(C )。 A) 基类的构造函数B) 派生类中子对象 的初始化 C) 基类中子对象的初始化D) 派生类中一般数 据成员的初始化 12. 下列虚基类的声明中,正确的是: ( B ) A)class virtual B: public A B)class B: virtual public A C)class B: public A virtual D)virtual class B: public A 13..实现运行时的多态性采用(D ) A)重载函数B)构造函数C)析构函数D)虚函数 14. 若一个类中含有纯虚函数,则该类称为(D )

Java继承与多态实验报告课案

西安邮电大学 (计算机学院) 课内实验报告 实验名称:继承与多态 专业名称:计算机科学与技术 班级:计科1405班 学生姓名:高宏伟 学号:04141152 指导教师:刘霞林 实验日期:2016.10.13

一、实验目的 通过编程和上机实验理解Java 语言的继承和多态特性,掌握变量的隐藏、方法的覆盖、重载,掌握抽象类和接口的使用。 二、实验要求 1.编写体现类的继承性(成员变量、成员方法、成员变量隐藏)的程序。 2.编写体现类的多态性(成员方法重载)的程序。 3.编写体现类的多态性(构造方法重载)的程序。 4.编写使用接口的程序。 三、实验内容 (一)类的继承 1.创建公共类Student. (1)编写程序文件Student.java,源代码如下: public class Student { protected String name; //具有保护修饰符的成员变量 protected int number; void setData(String m,int h) //设置数据的方法 { name =m; number= h; } public void print() //输出数据的方法 { System.out.println(name+", "+number); } } (2)编译Student.java,产生类文件Student.class。 2.创建继承的类Undergraduate (1)程序功能:通过Student 类产生子类undergraduate,其不仅具有父类的成员变量 name(姓名)、number(学号),还定义了新成员变量academy(学院)、department (系)。在程序中调用父类的print 方法。 (2)编写Undergraduate 程序: class Undergraduate extends Student { 【代码1】//定义成员变量academy

面向对象的三大特性(封装-继承-多态)

一丶封装 1 权限修饰符 可以用来修饰成员变量和成员方法,对于类的权限修饰只可以用public和缺省default。 被public修饰的类可以在任意地方被访问;default类只可以被同一个包内部的类访问。 权限由大到小:public protected default(不写) private 被private修饰的成员只能在本类中访问,外界不能访问 2 set()/get()方法 (1)this关键字 a.可以用来调用变量,方法,构造方法; b.this.xx 理解为调用当前类的xx。 (2)成员变量和局部变量 1)在类中的位置不同 a:成员变量:在类中,方法外 b:局部变量:在方法声明上(形式参数),或者是在方法定义中

2)在内存中的位置不同 a:成员变量:在堆内存 b:局部变量:在栈内存 3)生命周期不同 a:成员变量:随着对象的创建而存在,随着对象的消失而消失 b:局部变量:随着方法调用而存在,随着方法的调用结束而消失 4)初始化值不同 a:成员变量:有默认值 b:局部变量:必须初始化值,否则报错!(在使用它之前,没有初始化) (3)set()/get()方法 当成员变量被private修饰时,不在本类中无法直接访问,便需要set()/get()方法来解决这个问题 3 封装性 封装:是面向对象的第一大特性,所谓封装,就是值对外部不可见(一般而言被private修饰),外部只能通过对象提供的接口(如set()/get()方法)来访问。 封装的好处:a.良好的封装能够减少耦合;

b.类内部的结构可以自己修改,对外部影响不大; c.可以对成员进行更精准的控制(防止出现与事实不符的情况); d.影藏实现细节。 注意:在开发中,类的成员变量全部要进行封装,封装之后通过set()/get()方法访问。 二丶继承extends 1 实现:通过 class Zi extends Fu{} 实现类的继承 (1)子类继承父类,父类中声明的属性,方法,子类都可以获取到; 当父类中有私有的属性方法时,子类同样可以获取到,由于封装性的设计,使得子类不能直接调用访问。 (2)子类除了可以继承父类的结构外,还可以定义直接特有的成员变量,成员方法; (3)Java中类的继承只支持单继承,一个类只能继承一个父类,父类可以有多个子类,但是可以多层继承; (4)子类不能继承父类的构造方法,可以间接的通过super关键字去访问父类的构造方法(super();); 在子类的构造方法中如果没有显示的调用父类的构造方法,会默认调用父类的无参构造(所以最好父类都写有无参构造方法)。 子类继承父类的访问特点:首先父类进行初始化,然后子类进行初始化;多层访问! 2 方法的重写(区别重载) 子类继承父类以后,若父类的方法对子类不适用,那么子类可以对父类方法重写(override) 规则:1)要求子类方法的返回值类型方法名(参数列表)与父类方法一致; 2)子类方法的权限修饰符不能小于父类方法的修饰权限; 3)若父类方法抛异常,那么子类方法抛的异常类型不能大于父类; 注意: a.当子类重写了父类的方法以后,可以用 super.方法调用父类被重写的内容; b.子父类的方法必须同为static或同为非static。 class Fu{ private int i; public Fu(){ //无参构造 super();可以省略 super(); } public Fu(int i){ //有参构造

2015.6.17--java第七章继承和多态章总结

第七章继承和多态 1.继承的作用: 它是面向对象的三大特征之一,可以解决编程代码冗余的问题,是实现代码重用的重要手段之一。继承是软件可重用性的一种表现,新类可以在不增加自身代码的情况下,通过从现有的类中继承其属性和方法,来充实自身内容,这种表现或行为就称为继承。这个时候新类称为子类,现有的类称为父类。它最基本的作用就是:使得代码可重用,增加软件的可扩充性。 2.继承的语法: [访问修饰符] class extends; a.通过extends实现继承subclass 是子类后者是父类。 b.如果访问修饰符是public,那么该类在整个项目可见。 不写访问修饰符,该类只在当前包可见。 c.子类可以从父类中继承,public和protected修饰的属性和方法,不论子类和父类是否在同一个包里。 d.子类还可以继承默认访问修饰符修饰的属性和方法,但是子类和父类必须在同一个包里。 E.子类无法继承父类的构造方法。

3.多重继承关系的初始化顺序: 父类属性>>父类构造方法>>子类属性>>子类构造方法。 4.实例化子类对象: A.创建该类的对象。 B.创建该类的子类的对象。 C.子类在实例化时,会首先执行父类的构造方法,然后才执行子类的构造方法。 5.子类继承父类时,构造方法的调用规则如下: a.如果子类的构造方法中没有通过super显式调用父类的有参构造方法,也没有通过this显式调用自身的其他构造方法,则系统默认先调用父类的无参构造方法,在这种情况下是否写入super语句,效果是一样的。 b.如果子类的构造方法中通过super显式调用父类的有参构造方法,那么将执行父类相应的构造方法,而不执行父类的无参构造方法。 c.如果子类的构造方法中通过this显式调用自身的其他构造

实验四 、类的继承与多态

实验四类的继承与多态 一、实验目的 1.理解类的继承概念和类的继承规则。根据实际需要正确的运用类的继承。 2.理解类的多态概念,掌握子类对父类方法的重新定义,理解方法的匹配调 用原则。 二、实验内容 1.模拟编写程序,理解类的继承、多态、继承和多态规则。 2. 独立编程,实现类的继承和多态。 三、实验过程 (一)继承 (1)模拟编写程序,理解类的继承,继承规则。 请编码实现动物世界的继承关系: (1)动物(Animal)具有行为:吃(eat)、睡觉(sleep)。 (2)动物包括:兔子(Rabbit),老虎(Tiger)。 (3)这些动物吃的行为各不相同(兔子吃草,老虎吃肉);但睡觉的行为是一致的。 请通过继承实现以上需求,并编写测试类AnimalTest进行测试。 1. 定义Animal类,代码如下: public class Animal { public void sleep(){ System.out.println("我正睡觉呢!"); }

public void eat(){} } 2. 定义Tiger类,代码如下: public class Tiger extends Animal { public void eat(){ System.out.println("我喜欢吃肉!"); } } 3. 定义Rabbit类,代码如下: public class Rabbit extends Animal { public void eat(){ System.out.println("我喜欢吃草和青菜!"); } } 4、定义AnimalTest类,代码如下: public class AnimalTest { public static void main(String[] args) { Tiger tiger = new Tiger(); tiger.sleep(); tiger.eat(); System.out.println(); Rabbit rabbit = new Rabbit();

Java为什么要继承、封装和多态

为什么要继承、封装和多态 1,为什么要继承 继承的好处是代码重用.哲学上讲事物都是有共性和特性的.把共性函数代码放入到父类中,把特性函数代码放入到子类中,当然是否共性要以参照点的标准.OO中所谓的抽象我自己理解为哲学中的共性在同一个行业中,他们各业务流程往往有很大的相似性,但往往我们都是到一个项目中就重新写一套流程代码,或者粘贴以前的代码.可能有很多代码都是以前写过的重复代码.造成重复劳动.如果采用继承应该这样,首先在父类中做一个基本上大部分行业项目都必要的简洁的主流程.在子类中针对具体项目的特殊性做主流程充分的完善的补充.这样在每个项目中,只针对项目的特殊性编写代码,大大降低重复劳动.当然根据具体流程的复杂多可以划分多的继承层次,呈现一种继承的树结构,但一定的要保证层次一定要有实际的意义. 2,为什么要封装 高内聚低偶合的思想简单的理解同一模块内的提高内聚,不同模块降低偶合.如果说一个类代表一个模块或是一个业务流,那么A类内部要提高内聚,类的属性可以看成是内的局部变量.提高数据的重用.公共函数尽量能被其他主函数,尽量达到功能内聚.如果说是A类和B类,并且是不同模块(也许是同一个模块下的两个子模块),那么B 是不能使用A的函数和属性的,紧进行数据偶合.封装的作用就体现在这里.

再现实中就项目中经常遇到这种情况,某项业务增加或修改一种类型的业务流,自己本身调试成功了,但是缺影响了此业务下其他的业务流,不得不测试所有得其他原本正常的业务流并不得不针对此做类型判断的补丁,这种补丁很肯能导致程序结构的不合理. 3.为什么要多态 个人认为多态的好处体现在代码的可扩充性,比如一个业务有多个业务流,为了区别不同的类型就的使用判断,那么新添加一个流程就得在过程执行主流程(往往是final,必然是面向过程的)中添加一个”if then”或是重载一个函数 在目前项目中mvc得控制器就是典型的多态.想想如果我们不使用多态那么怎么办?因为对PO对象是新添加的,那么必须针对新的PO 对象在代码中做IF判断,才能用他本身的对象类型指向他.造成过程执行主流程不断的要变更自己的代码. 总结: OO中继承和多态互相依赖又互相矛盾,没有继承就不可能有多态,多态的使用往往又在面向过程的代码中.继承是使子类可以使用父类的功能,而多态使父类能使用子类的功能. OO并非只是在编码阶段来处理,实际上在概要设计,详细设计,数据库设计的时候就应该OO的思想来设计.提高软件可重用性和可扩充性.对于想对一个行业做标准化产品软件而言,很重要.

继承和接口设计习题

第7章继承和接口设计 一.选择题 1.在C#中,一个类【】。 A) 可以继承多个类B) 可以实现多个接口 C) 在一个程序中只能有一个子类D) 只能实现一个接口 2.【】关键字用于在C#中从派生类中访问基类的成员。 A) new B) super C) this D) base 3.在定义类时,若希望类的某个方法能够在派生类中进一步改进,以满足派生类的不同需要,则应将该方法声明为【】。 A) new B) public C) virtual D) override 4.在C#中,在派生类中对基类的虚方法进行重写,要求在派生类的声明中使用【】。 A) new B) public C) virtual D) override 5.已知类B是由类A继承而来,类A中有一个名为M的非虚方法,现在希望在类B中也定义一个名为M的方法,且两方法的参数个数和类型完全相同,若希望编译时不出现警告信息,则在类B中声明该方法时,应使用【】关键字。 A) static B) new C) override D) virtual 6.假设类B继承了类A,下列说法错误的是【】。 A) 类B中的成员可以访问类A中的公有成员 B) 类B中的成员可以访问类A中的保护成员 C) 类B中的成员可以访问类A中的私有成员 D) 类B中的成员可以访问类A中的静态成员 7.关于多态,下列说法正确的是【】。 A) 多态实际上就是重载,它们本质上是以一样的 B) 多态可以分为编译时多态和运行时多态。前者的特点是在编译时就能确定要调用成 员方法的哪个版本,后者则是在程序运行时才能确定要调用成员方法的哪个版本。 C) 编译时多态是在程序运行时才能确定要调用成员方法的哪个版本,而运行时多态在 编译时就能确定要调用成员方法的哪个版本。 D) 多态和重载是两个完全不同的概念,前者是通过定义虚方法和重写虚方法来实现, 后者是通过对同一函数名编写多个不同的实现代码来实现。 8.下列关于接口的说法,正确的是【】。 A) 接口中定义的方法都必须是虚方法 B) 接口中定义的方法可以编写其实现代码 C) 继承接口的类可提供被继承接口中部分成员的实现代码 D) 接口中的所有方法都必须在其派生类中得到实现。 9.下列关于虚方法的描述中,正确的是【】。 A) 虚方法能在程序执行时动态确定要调用的方法,因此比非虚方法更灵活 B) 在定义虚方法时,基类和派生类的方法定义语句中都要带上virtual修饰符 C) 在重写基类的虚方法时,为消除隐藏基类成员的警告,需要带上new修饰符 D) 在重写基类的虚方法时,需要同时带上virtual和override修饰符 10.在C#中,以下关于抽象类的叙述中错误的是【】。 A) 抽象类可以包含抽象方法B) 含有抽象方法的类一定是抽象类

相关文档
相关文档 最新文档