面向对象程序设计C++课后题答案 下载本文

第一章:面向对象程序设计概述

[1_1]什么是面向对象程序设计?

面向对象程序设计是一种新型的程序设计范型。这种范型的主要特征是: 程序=对象+消息。

面向对象程序的基本元素是对象,面向对象程序的主要结构特点是:第一:程序一般由类的定义和类的使用两部分组成,在主程序中定义各对象并规定它们之间传递消息的规律。第二:程序中的一切操作都是通过向对象发送消息来实现的,对象接受到消息后,启动有关方法完成相应的操作。

面向对象程序设计方法模拟人类习惯的解题方法,代表了计算机程序设计新颖的思维方式。这种方法的提出是软件开发方法的一场革命,是目前解决软件开发面临困难的最有希望、最有前途的方法之一。

[1_2]什么是类?什么是对象?对象与类的关系是什么?

在面向对象程序设计中,对象是描述其属性的数据以及对这些数据施加的一组操作封装在一起构成的统一体。对象可以认为是:数据+操作

在面向对象程序设计中,类就是具有相同的数据和相同的操作的一组对象的集合,也就是说,类是对具有相同数据结构和相同操作的一类对象的描述。 类和对象之间的关系是抽象和具体的关系。类是多个对象进行综合抽象的结果,一个对象是类的一个实例。

在面向对象程序设计中,总是先声明类,再由类生成对象。类是建立对象的“摸板”,按照这个摸板所建立的一个个具体的对象,就是类的实际例子,通常称为实例。

[1_3]现实世界中的对象有哪些特征?请举例说明。 对象是现实世界中的一个实体,其具有以下一些特征: (1)每一个对象必须有一个名字以区别于其他对象。

1

(2)需要用属性来描述它的某些特性。

(3)有一组操作,每一个操作决定了对象的一种行为。

(4)对象的操作可以分为两类:一类是自身所承受的操作,一类是施加于其他对象的操作。例如:雇员刘名是一个对象 对象名:刘名 对象的属性:

年龄:36 生日:1966.10.1 工资:2000 部门:人事部 对象的操作:吃饭 开车

[1_4]什么是消息?消息具有什么性质?

在面向对象程序设计中,一个对象向另一个对象发出的请求被称为“消息”。当对象接收到发向它的消息时,就调用有关的方法,执行相应的操作。消息是一个对象要求另一个对象执行某个操作的规格的说明,通过消息传递才能完成对象之间的相互请求或相互协作。消息具有以下3个性质:

(1)同一个对象可以接收不同形式的多个消息,做出不同的响应。

(2)相同形式的消息可以传递给不同的对象,所做出的响应可以是不同的。 (3)消息的发送可以不考虑具体的接收者,对象可以响应消息,也可以不响应。 [1_5]什么是方法?消息和方法的关系是什么?

在面向对象程序设计中,要求某一对象作某一操作时,就向该对象发送一个响应的消息,当对象接收到发向它的消息时,就调用有关的方法,执行响应的操作。方法就是对象所能执行的操作。方法包括界面和方法体两部分。方法的界面也就是消息的模式,它给出了方法的调用协议;方法体则是实现某种操作的一系列计算步骤,也就是一段程序。在C++语言中方法是通过函数来实现的,称为成员函数。消息和方法的关系是:对象根据接收到的消息,调用相应的方法;反过来,有了方法,对象才能响应相应的消息。 [1_6]什么是封装和抽象?请举例说明。

2

在现实世界中,所谓封装就是把某个事物包围起来,使外界不知道该事物的具体内容。在面向对象程序设计中,封装是指把数据和实现操作的代码集中起来放在对象内部,并尽可能隐蔽对象的内部细节。对象好象是一个不透明的黑盒子,表示对象属性的数据和实现各个操作的代码都被封装在黑盒子里,从外面是看不见的,更不能从外面直接访问或修改这些数据及代码。使用一个对象的时候,只需要知道它向外界提供的接口形式而无需知道它的数据结构细节和实现操作的算法。封装机制可以将对象的使用者与设计者分开,使用者不必知道对象行为实现的细节,只需要使用设计者提供的接口让对象去做。

抽象是人类认识问题的最基本的手段之一。它忽略了一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象是对复杂世界的简单表示,抽象强调感兴趣的信息,忽略了不重要的信息。例如,设计一个学籍管理程序的过程中,考察某个学生对象时,只关心他的姓名、学好、成绩等,而对他的身高、体重等信息就可以忽略。以一般观点而言,抽象是通过特定的实例(对象)抽象共同性质以后形成概念的过程。抽象是对系统的简化描述或规范说明,它强调了系统中的一部分细节和特性,而忽略了其他部分。抽象包括两个方面:数据抽象和代码抽象(或称为行为抽象)。前者描述某类对象的属性或状况,也就是此类对象区别于彼类对象的特征物理量;后者描述了某类对象的共同行为特征或具有的共同操作。

在面向对象程序设计方法中,对一个具体问题的抽象分析的结果,是通过类来描述和实现的。现在以学生管理程序为例,通过对学生进行归纳、分析,抽取出其中的共性,可以得到如下的抽象描述:

共同的属性:姓名、学号、成绩等,他们组成了学生数据抽象部分。用C++语言的数据成员来表示,可以是: char *name; int number; float score;

3

共同的行为:数据录入、数据修改和数据输出等,这构成了学生的行为抽象部分,用C++语言的成员函数表示,可以是:input();modify();print(); 如果我们开发一个学生健康档案程序,所关心的特征就有所不同了。可见,即使对同一个研究对象,由于所研究问题的侧重点不同,就可能产生不同的抽象结果。 [1_7]什么是继承?请举例说明。

继承所表达的是对象类之间的相关关系,这种关系使得某类对象可以继承另一类对象的特征和能力。现实生活中,继承是很普遍和容易理解的。例如我们继承了父母的一些特征,如种族、血型、眼睛的颜色等,父母是我们所具有的属性的基础。继承所表达的是对象之间相关的关系。这种关系使得某一类可以继承另一个类的特征和能力。

[1_8]若类之间具有继承关系,则它们之间具有什么特征? (1)类间具有共享特征(包括数据和操作代码的共享) (2)类间具有差别或新增部分(包括非共享的数据和代码操作) (3)类间具有层次结构

假设有两个类A和B,若类B继承类A,则类B包含了类A的特征(包括数据和操作),同时也可以加入自己所特有的新特性。这时,我们称被继承类A为基类或父类或超类;而称继承类B为A类的派生类或子类。同时,我们还可以说,类B是从类A中派生出来的。 [1_9]什么是单继承、多继承?请举例说明。

从继承源上分,继承分为单继承和多继承。单继承是指每个派生类只直接继承了一个基类的特征。多继承是指多个基类派生出一个派生类的继承关系。多继承的派生类直接继承了不止一个基类的特征。例如:小孩的玩具车继承了车的一些特性,还继承了玩具的一些特征。 [1_10]什么是多态性?举例说明。

4

多态性也是面向对象程序设计的重要特性。它是指不同的对象收到相同的消息时产生不同的行为方式。例如我们同样双击windows系统桌面上的图标时,有的是打开多媒体播放器,有的是打开资源管理器。利用多态性,用户只需发送一般形式的消息,而将所有的实现留给接收消息的对象。对象根据所收到的消息做出相应的动作。

[1_11]什么是函数重载和运算符重载?为什么要使用重载?

重载一般包括函数重载和运算符重载。函数重载是指一个表示符可同时用于为多个函数命名,而运算符重载是指一个运算符可同时用于多种运算。也就是说,相同名字的函数或运算符在不同的场合可以表现出不同的行为。

使用重载的目的是为了更好地表达行为共享,这种行为共享就象将相似的操作划分在一起。使用重载可以使程序员在只知道操作的一般含义,而不知道操作的具体细节的情况下能正确地对某个对象使用一个操作。另外,使用重载的直接益处是减少了程序员记忆操作的名字的负担。

5

第二章::C++基础

[2_1]简述C++的主要特点

(1)C++保持与C的兼容,用C编写的软件可以用到C++中。

(2)用C++编写的程序可读性好,代码结构更合理,可直接地在程序中映射问 题空间的结构。

(3)生成代码的质量高。

(4)软件的可重用性、可扩充性、可维护性和可靠性有了明显的提高,从而节省了开发费用和时间。

(5)支持面向对象的机制,可方便地构造出模拟现实问题的实体和操作。 [2_2]下面是一个C程序,改写它,使它采用C++风格的i/o语句 改写如下:

#include main()

{ int a,b,d,min;

cout<<”enter two numbers: “; cin>>a; cin>>b; min=a>b?b:a; for(d=2;d

if((a%b)==0)&&((b%d)==0)) break; if(d==min)

{ cout<<”no common denominators\\n”; return 0; }

6

cout<<”the lowest common denominator is “<

[2_3]测试下面的注释是否有效?

此注释有效,单行注释中可以嵌套/*……….*/方式的注释。 [2_4]以下简单的C++程序不可能编译通过,为什么?

原因是:在程序中,当一个函数的定义在后,而对它的调用在前时,必须将该函数的原型写在调用语句之前,而在本程序中缺少函数原型语句。在语句:#include 后加上语句sum(int a,int b);就可以通过了。

[2_5](1)答:这两个函数原形是等价的,因为函数原型中的参数名可以缺省。 (2)答:这两个函数的第一行是不等价的,函数的第一行中必须包含参数名。 (3)答:这两个函数原型是等价的,因为在函数原型中未注明参数,C++认为该函数的参数表为空(void)

[2_6]答:输出结果为:10 20 因为f函数的参数是引用,所以修改k的值有效。 函数调用后,主函数中k的值变为10。由于m是对函数的引用,当m被赋 值为20时,k的值也变为20。

[2_7] 举例说明可以使用const替代#define以消除#define的不安全性 答:例如:#include #define A 2+4 #define B A*3 void main()

{ cout<

上面程序的运行结果是14而不是18,但很容易被认为是18。用const替代#define就能得到正确结果,从而消除了#define的不安全性。 #include

7

const A=2+4; const B=A*3; void main()

{ cout<

[2_8]答:使用内联函数的优点主要有两个:一是能加快代码的执行,减少调用 开销;二是能消除宏定义的不安全性。

[2_9] 用动态分配空间的方法计算Fibonacci数列的前20项并存储到动态分配的 空间中。

答:#include #include “stdio.h” void main()

{ int I,*p=new int[20];//动态分配20个整型空间 *p=1;

*(p+1)=1;//前面两个空间赋值1 cout<<*p<<”\\t”<<*(p+1)<<”\\t”; p=p+2;//p指向第三个空间 for(i=3;i<=20;i++) { *p=*(p-1)+*(p-2); cout<<*p<<”\\t”; if(i%5==0) cout<

结果:1 1 2 3 5

8

8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765

[2_10] 建立一个被称为sroot()的函数,返回其参数的二次方根。重载sroot()三次, 让它返回整数、长整数与双精度数的二次方根(计算二次方根时,可以使用标准 库函数sqrt()) #include #include

int sroot(int );long sroot(long);double sroot(double); double sqrt();//声明开方函数sqrt() void main()

{ int i,x;long l,y;double d,z; cin>>i; cin>>l; cin>>d; x=sroot(i); y=sroot(l); z=sroot(d); cout<

int sroot(int i)

{ return sqrt(i); } //i是整数 long sroot(long l)

{ return sqrt(l); } //l是长整型 double sroot(double d) { return sqrt(d); } //d是双精度 //敲进9 16 25 //输出3 4 5

9

习题[2_11] 编写C++风格的程序,解决百钱问题,将一元人民币兑换成1、2、5分的硬币,有多少种换法? #include void main() { int i,j,sum=0; for(i=0;i<=20;i++) for(j=0;j<=50;j++) if(100-5*i-2*j>=0) { sum++;

cout<<100-5*i-2*j<<”\\t”<

cout<<”sum is “<

习题[2_12] 编写C++风格的程序,用二分法求解f(x)=0的根 #include #include inline float f(float x)

{ return 2*x*x*x-4*x*x+3*x-6; } void main()

{ float left,right,middle,ym,yl,yr;

cout<<”pleass two number:”<>left>>right; yl=f(left); yr=f(right);

10

do

{ middle=(right+left)/2; ym=f(middle); if(yr*ym>0) { right=middle; Yr=ym; } else

{ left=middle; yl=ym; }

}while(fabs(ym)>=1e-6);

cout<<”\\nRoot is:”<

本例使用了内联函数f(x),因为在主函数中多次调用它,这样可以加快代码执行的速度。敲进两个数:-10 10 结果:Root is 2

[2_13]答:运行结果是:2 4 6 12 10 说明:本例使用的是返回引用的值,index(3)=12;语句的执行实际将a[3]赋值为12。

[2_14]答:运行结果为:101 说明:在语句::i=i+1;中赋值号左边::i的i单元是全 局变量,赋值号右边的i单元是局部变量i。所以执行该语句的结果是 将局部变量i的值+1(101)赋值给全局变量i

[2_15]答:结果是:10 10 说明:函数f(&a,b)中的第一个参数是引用,引用参数是一种按地址传递参数的方法,对其的调用是传地址调用;而第二个参数是变量参数,对它的调用是通常的传值调用。所以运行后,a的值被改为10,b的值不变,仍为10

11

[2_16]答:D

说明:int *p=new int(10);表示分配1个整型空间,初值为10 int *p=new int[10];表示分配10个整型空间 int *p=new int;表示分配1个整型空间

int *p=new int[10](0)想给一个数组分配内存空间时,对整个数组进行初始化,这是不允许的。

[2_17]答:D 说明:name被定义为指向常量的常指针,所以它所指的内容和本 身的内容都不能修改,而name[3]=?a?;修改了name所指的 常量,name=?lin?;和name=new char[5];修改了常指针,只有 D输出一个字符是正确的。

[2_18]答:A 说明:name被定义指向常量的指针,这是一个不能移动的固定指针,它所指的内容不能改变,但指针所指的数据可以改变,而name[3]=?q?;修改了name所指的内容,是正确的。name=”lin”; name=new char[5]; name=new char(?q?);以不同的方法修改了常指针,都是错误的。

[2_19]答:A 说明:name被定义指向常量的指针,不允许改变指针所指的常量,但指针本身的内容可以修改,而name[3]=?q?;修改了name所指的内容,是错误的。name==”lin” name=new char[5];和name=new char(?q?)以不同的方法修改了常指针,都是正确的。

[2_20]答:D 说明:C++中不能建立引用数组和指向引用的指针,也不能建立引用的引用。所以A、B、C是错误的,D是正确的。

12

第三章:类和对象(一)

[3_1]答:类声明的一般格式如下: class 类名 { public:

公有数据成员; 公有成员函数; protected: 保护数据成员; 保护成员函数; private:

私有数据成员; 私有成员函数;

};其中:class是声明类的关键字;类名是要声明的类的名字;后面的花括号表示出类声明的范围;最后的分号表示类声明结束。

[3_2]答:构造函数是一种特殊的成员函数,它主要用于为对象分配空间,进行初始化。构造函数具有一些特殊的性质: (1)构造函数的名字必须与类名相同

(2)构造函数可以有任意类型的参数,但不能指定返回类型。它有隐含的返回值,该值在系统内部使用。

(3)构造函数是特殊的成员函数,函数体可写在类体内,也可写在类体外。 (4)构造函数可以重载,即一个类中可以定义多个参数个数或参数类型不同的构造函数。

(5)构造函数被声明为公有函数,但它不能象其它成员函数那样被显示地调用,它是在定义对象的同时被调用的。

13

析构函数也是一种特殊的成员函数。它执行与构造函数相反的操作,通常用 于撤消对象时的一些清理任务,如释放分配给对象的内存空间等。析构函数有以下一些特点:

(1)析构函数与构造函数名字相同,但它前面必须加一个波浪号(~) (2)析构函数没有参数,不能指定返回类型,而且不能重载。因此在一个类中只能有一个析构函数。

(3)当撤消对象时,编译系统会自动地调用析构函数。

[3_3]答:B 说明:C++中对构造函数有一些规定:不能带返回值;可以不带 参数;也可以缺省定义;但构造函数的名字与类名必须完全相同。

[3_4]答:C 说明:C++中没有限定private、public、protected的书写次序。但 是,不能在类的声明中给数据成员赋初值,数据成员的数据类型 也不能是register(寄存器类型),没有用private、public、protected 定义的数据成员是私有成员。

[3_5]答:C 说明:C++中对析构函数也有一些规定:没有参数;不能重载;析构函数的名字是在类名前加“~”;析构函数不能指定返回类型。 [3_6]答:B 说明:构造函数的工作是在创建对象时执行的。

[3_7]答:语句”p1.age=30;”出现错误。因为age是私有数据成员,不能直接访问。 [3_8]答:第1个错误:printStu、setSno两个成员函数没有用public定义,则不 允许外部函数对对象进行操作。

第2个错误:成员函数在类外定义,应加上类名“Student::”。

第3个错误:setAge应在类中说明,并且在类外定义时,应加上类名”Student::”。 [3_9]答:语句”Point cpoint;”是错误的,它试图用私有的构造函数Point访问公有数据成员x和y,这是不对的。

[3_10]答:语句Stack stt;”应该带参数,因为当类中没有定义构造函数时,编译器会自动生成一个缺省的不带参数的构造函数。但是,如果类中有自己定义的构

14

造函数后,编译器将不再自动生成一个缺省的构造函数。例如:将上述语句改成“Stack stt(10);”就正确了。

[3_11]:下面是一个计数器的定义,请完成该类成员函数的实现 #include class counter { public:

counter(int number);//构造函数 void increment(); //给原值加1 void decrement(); ///给原值减1 int getvalue(); //取得计数器值 int print(); //显示计数 private: int value; };

counter::counter(int number)//构造函数定义 { value=number; }

void counter::increment()//给原值加1 { value++; }

void counter::decrement()//给原值减1 { value--; }

int counter::getvalue()//取得计数器值 { return value; }

int counter::print()//显示计数 { cout<<\is \ return 0;

15

} main() { int i; cin>>i; counter a(0); for(int j=0;j

counter b(10); for(int k=1;k

习题:[3_12]根据注释语句的提示,实现类Date的成员函数#include class Date { public:

void printDate(); //显示日期 void setDay(int d);//设置日期值 void setMonth(int m);//设置月的值

16

void setYear(int y);//设置年的值 private:

int day,month,year; };

void main() { Date testDay; testDay.setDay(5); testDay.setMonth(10); testDay.setYear(2003); testDay.printDate(); }

void Date::printDate()

{ cout<<\is \ cout<

void Date::setDay(int d) { day=d; }

void Date::setMonth(int m) { month=m; } void Date::setYear(int y) { year=y; }

习题:[3_13]下面定义了一个类date,根据主程序的提示,实现重载构造函数date()

#include #include

17

class date { public:

date(int d,int m,int y); date::date(); void show(); private:

int day,month,year; };

void date::show()

{ cout<

{ date idate(28,10,1949);//构造函数的参数为3个整数 idate.show();

date indate; //构造函数没有参数,数据通过键盘直接输入 indate.show(); return 0; }

//解:重载构造函数的实现如下: date::date(int d,int m,int y) { day=d; month=m; year=y; }

18

date::date()

{ cout<<\month_day_year:\\n\ cin>>day; cin>>month; cin>>year;

}//注意:敲数据时要如:8 回车 9回车 2005回车

习题:[3_14]建立类cylinder,cylinder的构造函数被传递了两个double值,分别表示圆柱体的半径和高度。用类cylinder计算圆柱体的体积,并存储在一个double变量中。在类cylinder中包含一个成员函数vol(),用来显示每个cylinder对象的体积。

#include class cylinder { public:

cylinder(double a,double b); void vol(); private: double r,h; double volume; };

cylinder::cylinder(double a,double b) { r=a; h=b;

volume=3.141592*r*r*h; }

void cylinder::vol()

{ cout<<\is: \}

19

void main() {

cylinder x(2.2,8.09); x.vol(); }

习题:[3_15]建立一个Stock类,含有股票代码和股票现价两个数据成员。用new自动为Stock类的对象分配内存,并将股票代码“600001”,现价8.89存入内存的相应域中。 #include #include class Stock { public:

void set(char *c,float pr); void print(); private:

char Stockcode[7]; float price; };

void Stock::set(char *c,float pr) { strcpy(Stockcode,c); price=pr; }

void Stock::print()

{ cout<

20

} main() { Stock *p;

p=new Stock; //为对象分配空间 if(!p) //判断分配是否成功 { cout<<\error.\ return 1; }

p->set(\为对象赋值 p->print(); //显示对象 delete p; return 0; }

习题:[3_16]声明一个栈类,利用栈操作实现将输入字符串反向输出的功能#include //#include //#include #include const int SIZE=10; class stack { public:

stack() //构造函数 { tos=0; }

void push(char ch);//将数据ch压入栈 char pop(); //将栈顶数据弹出栈

21

char stck[SIZE]; //数组,用于存放栈中数据SIZE上面赋值为10 int tos; //栈顶位置(数组下标) };

//stack::stack() //构造函数,初始化栈 //{ tos=0; }

void stack::push(char ch)//压入栈 {

if(tos==SIZE) {

cout<<\is full\栈是满的 return; }

stck[tos]=ch; tos++; }

char stack::pop()//弹出栈 {

if(tos==0)

{ cout<<\is empty\栈是空的 return 0; } tos--;

return stck[tos]; }

void main()

22

{ int i; char str[20]; char re_str[20];

cout<<\input a string: \ cin>>str; stack ss;

for(i=0;i

for(i=0;i

cout<<\string: \ cout<

附:用C写反序输出程序

步骤:打开VC系统,FileànewàFileàC++Source Fileà改变路径Location为本章的路径àFile处写文件名àokà开始写C程序à之后编译运行 #include #include //#include main()

{ int inverse(char str[]); //函数原型说明 char str[100]; printf(\string: \ scanf(\

23

inverse(str);

printf(\string: %s\\n\}

int inverse(char str[]) //函数定义 { char t; int i,j;

for(i=0,j=strlen(str);i

24

第四章:类和对象(二)

[4_1]什么是对象数组

所谓对象数组是指每一数组元素都是对象的数组,也就是说,若一个类有若干个对象,我们把这一系列的对象用一个数组来存放。对象数组的元素是对象,不仅具有数据成员,而且还有函数成员。

[4_2]什么是this指针?它的主要作用是什么?

C++为成员函数提供了一个名字为this的指针,这个指针称为自引用指针。每当创建一个对象时,系统就把this指针初始化为指向该对象。每当调用一个成员函数时,系统就自动把this指针作为一个隐含的参数传给该函数。不同的对象调用同一个成员函数时,C++编译器将根据成员函数的this指针所指向的对象来确定应该引用哪一个对象的数据成员。 [4_3]友元函数有什么作用?

友元函数不是当前类的成员函数,而是独立于当前类的外部函数,但它可以访问该类的所有对象的成员,包括私有成员和公有成员。通过友元函数可以在不放弃私有数据安全的情况下,使得类外部的函数能够访问类中的私有成员。 当一个函数需要访问多个类时,友元函数非常有用,普通的成员函数只能访问其所属的类,但是多个类的友元函数能够访问相应的所有类的数据。此外,在某些情况,例如运算符被重载时,需要用到友元函数。

[4_4]假设在程序中已经声明了类point,并建立了其对象p1和p2。请回答以下 几个语句有什么区别?

(1)point p1,p2; 用带缺省参数的构造函数或不带参数的构造函数,定义了point 类的2个对象p1和p2。

(2)point p2=p1; 依据已存在的对象p1,用赋值形式调用拷贝构造函数,创建对 象p2。

25

(3)point p2(p1); 依据已存在的对象p1,用显示调用拷贝构造函数,创建对象 p2

(4)p2=p1; 对象赋值语句,将对象p1数据成员的值拷贝到对象p2中。 [4_5]在下面有关静态成员函数的描述中,正确的是(B)见书133页

说明:C++中规定在建立对象前,就可以为静态数据成员赋值。同时规定在静态 成员函数中不能使用this指针。静态成员函数在类外定义时,不需要用 static前缀。静态成员函数即可以在类内定义也可以在类外定义 [4_6]在下面有关友元函数的描述中,正确的说法是(A)(134)

说明:在C++中友元函数是独立于当前类的外部函数,一个友元函数可以同时定 义为两个类的友元函数。友元函数即可以在类的内部也可以在类的外部定 义,而在外部定义友元函数时,不必加关键字friend [4_7]友元函数的作用之一是(A)(134)

说明:由于友元函数可以直接访问对象的私有成员,所以友元的作用是提高程序 运行的效率。

[4_8]指出下面程序的错误,并说明原因:答案是将其中对应的2条改成: cout<get_sno()<<”\\n”; 因为:非静态成员函数的调用方法与静态成员函数的调用方法不同。 [4_9]答:

#include #include class CTest { public: const int y2;

CTest(int i1,int i2):y1(i1),y2(i2)

26

{ y1=10; //错误,y1是调用const定义的,不能修改 x=y1; }

int readme() const; // ….. private: int x; const int y1; };

int CTest::readme() const { int i; i=x;

x++; //错误,函数定义用了const,表示该函数不能修改对象 return x; }

void main() { CTest c(2,8); int i=c.y2;

c.y2=i; //错误,y2是常量,不能修改 i=c.y1; //错误,y1是私有变量,不能直接存取 }

[4_10]答:

#include #include class CTest

27

{ public: CTest () { x=20; } void use_friend(); private: int x;

friend void friend_f(CTest fri); };

void friend_f(CTest fri) { fri.x=55; }

void CTest::use_friend() { CTest fri;

this->friend_f(fri); //错误。友元函数不是成员函数,所以不能用this->调用友//元函数 ::friend_f(fri); }

void main() { CTest fri,fri1;

fri.friend_f(fri); //错误,友元函数不是成员函数,所以不能用对象.函数名调 //用友元函数 friend_f(fri1); }

[4_11]答:

#include #include

28

class CTest { public: CTest() { x=20; } void use_this(); private: int x; };

void CTest::use_this() { CTest y,*pointer;

this=&y; //错误,不能对this直接赋值

*this.x=10; //错误,按优先级原句的含义是*(this.x)=10,显然不对,正确的写 //法是(*this).x=10;或this->x=10; pointer=this; pointer=&y; }

void main() { CTest y;

this->x=235; //错误,this的引用不能在外部函数中,只能在内部函数中。 }

[4_12]答:运行结果是: 10,20 30,48 50,68 70,80

29

90,16 11,120

[4_13]答:运行结果是: Constructing 10 Destructing. 100 Destructing

[4_14]答:运行结果是: 3 objects in existence

4 objects in existence after allocation 3 objects in existence after deletion

说明:这个程序使用静态数据成员追踪记载创建对象的个数。完成这一工作的方 法就是每创建一个对象就调用构造函数一次。每调用构造函数一次,静态 数据成员total就增加1,每撤消一个对象就调用析构函数一次。每调用析 构函数一次,静态数据成员total就减少1。 [4_15] 运行结果是: Here?s the program output. Let?s generate some stuff… Counting at 0 Counting at 1 Counting at 2 Counting at 3 Counting at 4 Counting at 5

30

Counting at 6 Counting at 7 Counting at 8 Counting at 9

说明:在程序中main()只包括了一个return语句,但竟然有内容输出!什么时候 调用了构造函数?构造函数在对象被定义时调用。那么对象anObject是何时被调用的呢?是在main()之前,语句”test anObject”处。因此,anObject的构造函数是先于main()被调用的。在main()之前的所有全局变量都是在main()开始之前就建立了的。应该尽可能避免使用全局变量,因为全局变量有可能引起名称冲突,使程序的执行结果和预想的不一样。

[4_16] [4_17]构建一个类book,其中含有2个私有数据成员qu和price,建立一 个有5个元素的数组对象,将qu初始化为1~5,将price初始化为qu的10 倍。显示每个对象的qu*price 答案见下: #include class book { public:

book(int a,int b) { qu=a; price=b; } void show_money() { cout<

int qu,price; }; main()

{ book ob[5]={ book(1,10),book(2,20),

31

book(3,30),book(4,40),book(5,50) }; //16题用下面语句 /*int i;

for(i=0;i<5;i++) ob[i].show_money(); return 0;*/ //17题用下面的语句 int i; book *p; p=&ob[4]; for(i=0;i<5;i++) { p->show_money(); p--; } return 0; }

[4_18]使用C++的 见书139页题 #include //#include class toy { public: toy(){ } toy(int p,int c) { price=p; count=c;

32

}

void input(int p,int c); void compute(); void print(); private: int price; int count; long total; };

void toy::input(int p,int c) { price=p; count=c; }

void toy::compute()

{ total=(long)price*count; } void toy::print()

{ cout<<\count=\total=\} void main()

{ toy te(2,100);//测试构造函数 toy *ob; ob=new toy[6]; ob[0].input(25,130); ob[0].input(25,130); ob[1].input(30,35); ob[2].input(15,20);

33

ob[3].input(25,120); ob[4].input(45,10); ob[5].input(85,65); for(int i=0;i<6;i++) ob[i].compute(); // clrscr(); for(i=0;i<6;i++) ob[i].print(); delete ob; }

[4_19]构建一个类stock 见书139页 答案如下:#include #include #include const int SIZE=80; class stock { public: stock()

{ strcpy(stockcode,\\}

stock(char code[],int q=1000,float p=8.98) { strcpy(stockcode,code); quan=q; price=p; }

void print(void)

34

{ cout<stockcode;

cout<<\\\ } private:

char stockcode[SIZE]; int quan; float price; }; main() {

stock st1(\ st1.print(); stock st2;

char stockc[]=\ st2=stockc; st2.print(); return 0; }

说明:执行以下语句,可在定义对象的同时,给数据成员设置初值。但构造函数 的参数必须定义为缺省值: stock st2;

char stockc[]=”600002”; st2=stockc; st2.print();

定义不含第2和第3个参数的对象st2时,quan的值为1000,price的值

35

为8.98

[4_20]编写一个有关股票的程序,见书139页题 #include #include #include class shen_stock; //深圳类 class shang_stock //上海类 { public:

shang_stock(int g,int s,int p); //构造函数

friend void shang_count(const shang_stock ss);//计算上海的 //股票总数

friend void count(const shang_stock ss,const shen_stock zs); //计算上海和深圳的股票总数 private:

int general; //普通股票个数 int st; //ST股票个数 int pt; //PT股票个数 };

shang_stock::shang_stock(int g,int s,int p)//构造函数 { general=g; st=s; pt=p; }

class shen_stock

{ int general; //普通股票个数

36

int st; //ST股票个数 int pt; //PT股票个数 public:

shen_stock(int g,int s,int p);//构造函数

friend void shen_count(const shen_stock es);//计算深圳的股票总数 friend void count(const shang_stock ss,const shen_stock zs); //计算上海和深圳的股票总数 };

shen_stock::shen_stock(int g,int s,int p)//构造函数 { general=g; st=s; pt=p; } main()

{ shang_stock shanghai(1600,20,10);//建立对象

//表示上海有1600支股票,20支ST股票,10支PT股票 shen_stock shenzhen(1500,15,8); //建立对象

//表示深圳有1500支股票,15支ST股票,8支PT股票 shang_count(shanghai);//计算上海的股票总数 shen_count(shenzhen);//计算深圳的股票总数

count(shanghai,shenzhen);//计算上海和深圳的股票总数 return 0; }

void shang_count(const shang_stock ss)//计算上海的股票总数 { int s;

37

cout<<\of shanghai are \}

void shen_count(const shen_stock es)//计算深圳的股票总数 { cout<<\of shanghai are \} void count(const shang_stock ss,const shen_stock es) //计算上海和深圳的股票总数 { int s;

s=es.general+es.st+es.pt+ss.general+ss.st+ss.pt; cout<<\of shanghai and shenzhen \}

38

第五章:继承与派生类

[5_1]答:类的继承方式有public(公有继承)、protected(保护继承)和private(私有继承)3种,不同的继承方式导致原来具有不同访问属性的基类成员在派生类中的访问属性也有所不同。 (1)基类中的私有成员

无论哪种继承方式,基类中的私有成员不允许派生类继承,即在派生类中是不可直接访问的。

(2)基类中的公有成员

当类的继承方式为公有继承时,基类中的所有公有成员在派生类中仍以公有成员的身份出现;当类的继承方式为私有继承时,基类中的所有公有成员在派生类中都以私有成员的身份出现;当类的继承方式为保护继承时,基类中的所有公有成员在派生类中都是以保护成员的身份出现。 (3)基类中的保护成员

当类的继承方式为公有继承时,基类中的所有保护成员在派生类中仍以保护成员的身份出现;当类的继承方式为私有继承时,基类中的所有保护成员在派生类中都是以私有成员的身份出现:当类的继承方式为保护继承时,基类中的所有保护成员在派生类中仍以保护成员的身份出现。

[5_2]答:派生类不能直接访问基类的私有成员,但是可以通过基类提供的公有 成员函数间接地访问基类的私有成员

[5_3]答:保护成员可以被派生类的成员函数访问,但是对于外界是隐藏起来的,外部函数不能访问它。因此,为了便于派生类的访问,可以将基类私有成员中需要提供给派生类访问的成员定义为保护成员。C++规定,派生类对于保护成员的继承与公有成员的继承很相似,也分为两种情况:若为公有派生,则基类中的保

39

护成员在派生类中也为保护成员;若为私有派生,则基类中的保护成员在派生类中成为私有成员。

[5_4]答:通常情况下,当创建派生类对象时,首先执行基类的构造函数,随后再执行派生类的构造函数;当撤消派生类对象时,则先执行派生类的析构函数函数,随后再执行基类的析构函数。

[5_5]答:当基类的构造函数没有参数或没有显示定义构造函数时,派生类可以 不向基类传递参数,甚至可以不定义构造函数。当基类含有带参数的构造函数时,派生类必须定义构造函数,以提供把参数传递给基类构造函数的途径。 派生类构造函数的一般格式如下:

派生类构造函数名(参数表):基类构造函数名(参数表) { }

其中基类构造函数的参数,通常来源于派生类构造函数的参数表,也可以用常数值。由于析构函数是不带参数的,在派生类中是否定义析构函数与它所属的基类无关,基类的析构函数不会因为派生类没有析构函数而得不到执行,它们各自是独立的。

[5_6]答:当一个派生类具有多个基类时,这种派生方法称为多继承。多继承构造函数的执行顺序与单继承构造函数的执行顺序相同,也是遵循先执行基类的构造函数,再执行对象成员的构造函数,最后执行派生类构造函数的原则。在多个基类之间则严格按照派生类声明时从左到右的顺序来排列先后。而析构函数的执行顺序刚好与构造函数的执行顺序相反。

[5_7]在类的派生中为何要引入虚基类?虚基类构造函数的调用顺序是如何规定的?

当引用派生类的成员时,首先在派生类自身的作用域中寻找这个成员,如果没有找到,则到它的基类中寻找。如果一个派生类是从多个基类派生来的,而这些基

40

类又有一个共同的基类,则在这个派生类中访问这个共同的基类中的成员时,可能会产生二义性。为了解决这种二义性,C++引入了虚基类的概念。 虚基类构造函数的调用顺序是:

(1)若同一层次中包含多个虚基类,这些虚基类的构造函数按它们的说明的先后次序调用。

(2)若虚基类由非虚基类派生而来,则仍然先调用基类构造函数,再调用派生类的构造函数。

(3)若同一层次中同时包含虚基类和非虚基类,应先调用虚基类的构造函数,再调用非虚基类的构造函数,最后调用派生类构造函数。 [5_8]答:A

[5_9]答:C 说明:C++中继承方式有3种:私有继承、保护继承和公有继承。 [5_10]答:C

[5_11]答:C 说明:set_price()和print_price()在派生类中是私有成员函数,不能被派生类对象直接访问,所以A和D是错误的。code是私有数据成员,不能直接存取,所以B也是错误的。

[5_12]答:B 说明:code是私有数据成员,不能直接存取,所以B是错误的。 [5_13]答:运行结果是: Person Name=Angel Student Name=Angel Employee Name=Angel SideLine Name=Angel

说明:(1)从结果看出,程序中的虚基类Person的构造函数只执行了一次。当类SideLine的构造函数调用了虚基类Person的构造函数之后,类Student和类Employee对类SideLine的构造函数的调用被忽略了,这是初始化虚基类和非虚

41

基类不同的地方。(2)多重继承的构造函数执行顺序遵循先执行基类的构造函数,再执行对象成员的构造函数,最后执行派生类构造函数的原则。 [5_14]运行结果是:40 50 10 20 30 40 50 60 [5_15]修改后的程序如下: #include #include class table { protected: int i,j; public:

table(int p,int q) { i=p; j=q; } void ascii(void); };

void table::ascii(void) { int k=1; for(;i

{ cout<

cout<<\}

class der_table:public table

42

{ protected: char *c; public:

der_table(int p,int q,char *m):table(p,q) { c=m; } void print(void); };

void der_table::print(void) { cout<

void main()

{ der_table ob('a','z',\ ob.print(); }

[5_16]程序编制如下: #include #include class time { public:

time(int h,int m,int s) { hours=h; minutes=m; seconds=s; }

43

virtual void display()

{ cout<

int hours,minutes,seconds; };

class date { public:

date(int m,int d,int y) { month=m; day=d; year=y; }

virtual void display()

{ cout<

int month,day,year; };

class birthtime:public time,public date//加此类 { public:

birthtime(char *cn,int mm,int dd,int yy,int hh,int mint,int ss) :time(hh,mint,ss),date(mm,dd,yy) { strcpy(childname,cn); } void display()

{ cout<

44

cout<

time::display(); } protected:

char childname[20]; };

void main()

{ birthtime yx(\ yx.display(); }

[5_17]程序如下 #include #include class animal { public: animal()

{ name=NULL; } animal(char *n); ~animal()

{ delete name; } virtual void whoami(); protected:

char *name; };

class cat:public animal

45

{ public:

cat():animal() { }

cat(char *n):animal(n) { } void whoami(); };

class tiger:public cat { public:

tiger():cat() { }

tiger(char *n):cat(n) { } void whoami(); };

animal::animal(char *n)

{ name=new char [strlen(n)+1]; strcpy(name,n); }

void animal::whoami() { cout<<\void cat::whoami()

{ cout<<\void tiger::whoami()

{ cout<<\

46

void main() //添加的主函数 { cat cat(\ cat.whoami();

tiger tiger(\ tiger.whoami(); }

[5_18]实现本题功能的程序如下: #include class building { public:

building(int f,int r,double ft) { floors=f; rooms=r; footage=ft; } protected:

int floors;//层数 int rooms; //房间数 double footage;//平方数 };

class house:public building { public:

house(int f,int r,double ft,int br,int bth) nbsp; :building(f,r,ft)

{ bedrooms=br; bathrooms=bth; }

47

void show()

{ cout<<\\ cout<<\\<<'\\n'; cout<<\footage: \ cout<<\\ cout<<\\ } private:

int bedrooms;//卧室数 int bathrooms;//浴室数 };

class office:public building { public:

office(int f,int r,double ft,int p,int ext) :building(f,r,ft)

{ phones=p; extinguishers=ext; } void show()

{ cout<<\\ cout<<\\

cout<<\footage: \ cout<<\\ cout<<\extinguishers: \ cout<

int phones;//电话数 int extinguishers;//灭火器数 };

48

main()

{ house h_ob(2,12,5000,6,4); office o_ob(4,25,12000,30,8); cout<<\\\n\ h_ob.show(); cout<<\\\n\ o_ob.show(); return 0; }

[5_19]实现本题功能的程序如下: #include #include const int l=80; class person { public: void input()

{ cout<<\input name: \ cin>>name;

cout<<\certificate no: \ cin>>id; }

void print()

{ cout<<\certificate no: \ void printname()

{ cout<

char name[l],id[l]; };

49

} class stud { public: void input()

{ cout<<\input address: \ cin>>addr;

cout<<\input telphone no: \ cin>>tel; }

void print()

{ cout<<\address: \ cout<<\telphone no: \ } private:

char addr[l]; char tel[l]; };

class student:private person { public: void input() { person::input();

cout<<\input years old: \ cin>>old;

cout<<\input score no: \ cin>>sno; }

void print() { person::print();

cout<<\tears old: \

50