?----------------------- Page 1-----------------------
第一章:面向对象程序设计概述
[1_1]什么是面向对象程序设计?
面向对象程序设计是一种新型的程序设计范型。这种范型的主要特征是:
程序=对象+消息。
面向对象程序的基本元素是对象,面向对象程序的主要结构特点是:第一:程序
一般由类的定义和类的使用两部分组成,在主程序中定义各对象并规定它们之间
传递消息的规律。第二:程序中的一切操作都是通过向对象发送消息来实现的,
对象接受到消息后,启动有关方法完成相应的操作。
面向对象程序设计方法模拟人类习惯的解题方法,代表了计算机程序设计新颖的
思维方式。这种方法的提出是软件开发方法的一场革命,是目前解决软件开发面
临困难的最有希望、最有前途的方法之一。
[1_2]什么是类?什么是对象?对象与类的关系是什么?
在面向对象程序设计中,对象是描述其属性的数据以及对这些数据施加的一组操
作封装在一起构成的统一体。对象可以认为是:数据+操作
在面向对象程序设计中,类就是具有相同的数据和相同的操作的一组对象的集
合,也就是说,类是对具有相同数据结构和相同操作的一类对象的描述。
类和对象之间的关系是抽象和具体的关系。类是多个对象进行综合抽象的结果,
一个对象是类的一个实例。
在面向对象程序设计中,总是先声明类,再由类生成对象。类是建立对象的“摸
板”,按照这个摸板所建立的一个个具体的对象,就是类的实际例子,通常称为
实例。
[1_3]现实世界中的对象有哪些特征?请举例说明。
对象是现实世界中的一个实体,其具有以下一些特征:
----------------------- Page 2-----------------------
(1)每一个对象必须有一个名字以区别于其他对象。
(2)需要用属性来描述它的某些特性。
(3)有一组操作,每一个操作决定了对象的一种行为。
(4 )对象的操作可以分为两类:一类是自身所承受的操作,一类是施加于其他
对象的操作。例如:雇员刘名是一个对象
对象名:刘名
对象的属性:
年龄:36 生日:1966.10.1 工资:2000 部门:人事部
对象的操作:吃饭 开车
[1_4]什么是消息?消息具有什么性质?
在面向对象程序设计中,一个对象向另一个对象发出的请求被称为“消息”。当对
象接收到发向它的消息时,就调用有关的方法,执行相应的操作。消息是一个对
象要求另一个对象执行某个操作的规格的说明,通过消息传递才能完成对象之间
的相互请
求或相互协作。消息具有以下3 个性质:
(1)同一个对象可以接收不同形式的多个消息,做出不同的响应。
(2)相同形式的消息可以传递给不同的对象,所做出的响应可以是不同的。
(3)消息的发送可以不考虑具体的接收者,对象可以响应消息,也可以不响应。
[1_5]什么是方法?消息和方法的关系是什么?
在面向对象程序设计中,要求某一对象作某一操作时,就向该对象发送一个响应
的消息,当对象接收到发向它的消息时,就调用有关的方法,执行响应的操作。
方法就是对象所能执行的操作。方法包括界面和方法体两部分。方法的界面也就
是消息的模式,它给出了方法的调用协议;方法体则是实现某种操作的一系列计
算步骤,也就是一段程序。在C++语言中方法是通过函数来实现的,称为成员函
数。消息和方法的关系是:对象根据接收到的消息,调用相应的方法;反过来,
有了方法,对象才能响应相应的消息。
----------------------- Page 3-----------------------
[1_6]什么是封装和抽象?请举例说明。
在现实世界中,所谓封装就是把某个事物包围起来,使外界不知道该事物的具体
内容。在面向对象程序设计中,封装是指把数据和实现操作的代码集中起来放在
对象内部,并尽可能隐蔽对象的内部细节。对象好象是一个不透明的黑盒子,表
示对象属性的数据和实现各个操作的代码都被封装在黑盒子里,从外面是看不见
的,更不能从外面直接访问或修改这些数据及代码。使用一个对象的时候,只需
要知道它向外界提供的接口形式而无需知道它的数据结构细节和实现操作的算
法。封装机制可以将对象的使用者与设计者分开,使用者不必知道对象行为实现
的细节,只需要使用设计者提供的接口让对象去做。
抽象是人类认识问题的最基本的手段之一。它忽略了一个主题中与当前目标无关
的那些方面,以便更充分地注意与当前目标有关的方面。抽象是对复杂世界的简
单表示,抽象强调感兴趣的信息,忽略了不重要的信息。例如,设计一个学籍管
理程序的过程中,考察某个学生对象时,只关心他的姓名、学好、成绩等,而对
他的身高、体重等信息就可以忽略。以一般观点而言,抽象是通过特定的实例(对
象)抽象共同性质以后形成概念的过程。抽象是对系统的简化描述或规范说明,
它强调了系统中的一部分细节和特性,而忽略了其他部分。抽象包括两个方面:
数据抽象和代码抽象(或称为行为抽象)。前者描述某类对象的属性或状况,也
就是此类对象
区别于彼类对象的特征物理量;后者描述了某类对象的共同行为特
征或具有的共同操作。
在面向对象程序设计方法中,对一个具体问题的抽象分析的结果,是通过类来描
述和实现的。现在以学生管理程序为例,通过对学生进行归纳、分析,抽取出其
中的共性,可以得到如下的抽象描述:
共同的属性:姓名、学号、成绩等,他们组成了学生数据抽象部分。用C++语言
的数据成员来表示,可以是:
char *name; int number; float score;
----------------------- Page 4-----------------------
共同的行为:数据录入、数据修改和数据输出等,这构成了学生的行为抽象部分,
用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]什么是多态性?举例说明。
----------------------- Page 5-----------------------
多态性也是面向对象程序设计的重要特性。它是指不同的对象收到相同的消
息时产生不同的行为方式。例如我们同样双击windows 系统桌面上的图标时
,
有的是打开多媒体播放器,有的是打开资源管理器。利用多态性,用户只需发送
一般形式的消息,而将所有的实现留给接收消息的对象。对象根据所收到的消息
做出相应的动作。
[1_11]什么是函数重载和运算符重载?为什么要使用重载?
重载一般包括函数重载和运算符重载。函数重载是指一个表示符可同时用于
为多个函数命名,而运算符重载是指一个运算符可同时用于多种运算。也就是说,
相同名字的函数或运算符在不同的场合可以表现出不同的行为。
使用重载的目的是为了更好地表达行为共享,这种行为共享就象将相似的
操作划分在一起。使用重载可以使程序员在只知道操作的一般含义,而不知道操
作的具体细节的情况下能正确地对某个对象使用一个操作。另外,使用重载的直
接益处是减少了程序员记忆操作的名字的负担。
----------------------- Page 6-----------------------
第二章::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;
}
----------------------- Page 7-----------------------
cout<<”the lowest common denominator is “<
return 0;
}
[2_3]测试下面的注释是否有效?
此注释有效,单行注释中可以嵌套/*……….*/方式的注释。
[2_4]以下简单的C++程序不可能编译通过,为什么?
原因是:在程序中,当一个函数的定义在后,而对它的调用在前时,必须将该函
数的原型写在调用语句之前,而在本程序中缺少函数原型语句。在语句:#inclu
de
[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 替代#defi
ne 就能得到正确结果,从而消除了#define 的不安全性。
#include
----------------------- Page 8-----------------------
const A=2+4;
const B=A*3;
void main()
{ cout<
运行结果为 18。
[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<
p++;//指向下一个空间
}
}
结果:1 1 2 3 5
----------------------- Page 9-----------------------
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
----------------------- Page 10-----------------------
习题[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:”<
cin>>left>>right;
yl=f(left);
yr=f(right);
----------------------- Page 11-----------------------
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 说明:本例使用的是返回引用的值,inde
x(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
----------------------- Page 12-----------------------
[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 ch
ar(‘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 是正确的。
----------------------- Page 13-----------------------
第三章:类和对象(一)
[3_1]答:类声明的一般格式如下:
class 类名
{ public:
公有数据成员;
公有成员函数;
protected:
保护数据成员;
保护成员函数;
private:
私有数据成员;
私有成员函数;
};其中:class 是声明类的关键字;类名是要声明的类的名字;后面的花括号表
示出类声明的范围;最后的分号表示类声明结束。
[3_2]答:构造函数是一种特殊的成员函数,它主要用于为对象分配空间,进行
初始化。构造函数具有一些特殊的性质:
(1)构造函数的名字必须与类名相同
(2)构造函数可以有任意类型的参数,但不能指定返回类型。它有隐含的返回
值,该值在系统内部使用。
(3)构造函数是特殊的成员函数,函数体可写在类体内,也可写在类体外。
(4 )构造函数可以重载,即一个类中可以定义多个参数个数或参数类型不同的
构造函数。
(5)构造函数被声明为公有函数,但它不能象其它成员函数那样被显示地调用,
它是在定义对象的同时被调用的。
----------------------- Page 14-----------------------
析构函数也是一种特殊的成员函数。它执行与构造函数相反的操作,通常用
于撤消对象时的一些清理任务,如释放分配给对象的内存空间等。析构函数有以
下一些特点:
(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;”应该带参数,因为当类中没有定义构造函数时,编译
器会自动生成一个缺省的不带参数的构造函数。但是,如果类中有自己定义的构
----------------------- Page 15-----------------------
造函数后,编译器将不再自动生成一个缺省的构造函数。例如:将上述语句改成
“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<<"value is "<
return 0;
----------------------- Page 16-----------------------
}
main()
{ int i;
cin>>i;
counter a(0);
for(int j=0;j
{ a.increment();
a.getvalue();
a.print();
}
counter b(10);
for(int k=1;k
{ b.decrement();
b.getvalue();
b.print();
}
return 0;
}
习题:[3_12]根据注释语句的提示,实现类Date 的成员函数
#include
class Date
{ public:
void printDate(); //显示日期
void setDay(int d);//设置日期值
void setMonth(int m);//设置月的值
----------------------- Page 17-----------------------
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<<"\nDate 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
----------------------- Page 18-----------------------
class date
{ public:
date(int d,int m,int y);
date::date();
void show();
private:
int day,month,year;
};
void date::show()
{ cout<
cout<
}
main()
{ 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;
}
----------------------- Page 19-----------------------
date::date()
{ cout<<"Enter month_day_year:\n";
cin>>day;
cin>>month;
cin>>year;
}//注意:敲数据时要如:8 回车 9 回车 2005 回车
习题:[3_14]建立类cylinder,cylinder 的构造函数被传递了两个double 值,分别
表示圆柱体的半径和高度。用类cylinder 计算圆柱体的体积,并存储在一个dou
ble 变量中。在类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<<"volume is: "<
----------------------- Page 20-----------------------
void main()
{
cylinder x(2.2,8.09);
x.vol();
}
习题:[3_15]建立一个 Stock 类,含有股票代码和股票现价两个数据成员。用ne
w 自动为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,floa
t pr)
{ strcpy(Stockcode,c);
price=pr;
}
void Stock::print()
{ cout<
cout<<"\n";
----------------------- Page 21-----------------------
}
main()
{ Stock *p;
p=new Stock; //为对象分配空间
if(!p) //判断分配是否成功
{ cout<<"Allocation error.";
return 1;
}
p->set("600001",8.89);//为对象赋值
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(); //将栈顶数据弹出栈
----------------------- Page 22-----------------------
char stck[SIZE]; //数组,用于存放栈中数据 SIZE 上面赋值为 10
int tos; //栈顶位置(数组下标)
};
//stack::stack() //构造函数,初始化栈
//{ tos=0; }
void stack::push(char ch)//压入栈
{
if(tos==SIZE)
{
cout<<"Stack is full";//栈是满的
return;
}
stck[tos]=ch;
tos++;
}
char stack::pop()//弹出栈
{
if(tos==0)
{ cout<<"Stack is empty";//栈是空的
return 0;
}
tos--;
return stck[tos];
}
void main()
----------------------- Page 23-----------------------
{ int i;
char str[20];
char re_str[20];
cout<<"\nplease input a string: ";
cin>>str;
stack ss;
for(i=0;i
ss.push(str[i]);
for(i=0;i
re_str[i]=ss.pop();
re_str[i]='\0';
cout<<"\nreverse 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("Input string: ");
scanf("%s",str);
----------------------- Page 24-----------------------
inverse(str);
printf("Inverse string: %s\n",str);
}
int inverse(char str[]) //函数定义
{ char t;
int i,j;
for(i=0,j=strlen(str);i
{ t=str[i];
str[i]=str[j-1];
s
tr[j-1]=t;
}
return 0;
}
----------------------- Page 25-----------------------
第四章:类和对象(二)
[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 。
----------------------- Page 26-----------------------
(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<
cout<
因为:非静态成员函数的调用方法与静态成员函数的调用方法不同。
[4_9]答:
#include
#include
class CTest
{ public:
const int y2;
CTest(int i1,int i2):y1(i1),y2(i2)
----------------------- Page 27-----------------------
{ 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
----------------------- Page 28-----------------------
{ 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
----------------------- Page 29-----------------------
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
-----------------------
Page 30-----------------------
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
----------------------- Page 31-----------------------
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<
private:
int qu,price;
};
main()
{ book ob[5]={ book(1,10),book(2,20),
----------------------- Page 32-----------------------
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;
----------------------- Page 33-----------------------
}
void input(int p,in
t 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<<"price="<
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);
----------------------- Page 34-----------------------
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)
----------------------- Page 35-----------------------
{ cout<
cout<<" "<
}
private:
char stockcode[SIZE];
int quan;
float price;
};
main()
{
stock st1("600001",3000,8.89);
st1.print();
stock st2;
char stockc[]="600002";
st2=stockc;
st2.print();
return 0;
}
说明:执行以下语句,可在定义对象的同时,给数据成员设置初值。但构造函数
的参数必须定义为缺省值:
stock st2;
char stockc[]=”600002”;
st2=stockc;
st2.print();
定义不含第2 和第3 个参数的对象 st2 时,quan 的值为1000,price 的值
----------------------- Page 36-----------------------
为 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; //普通股票个数
----------------------- Page 37-----------------------
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;
----------------------- Page 38-----------------------
cout<<"stocks of shanghai are "<
}
void shen_count(const shen_stock es)//计算深圳的股票总数
{ cout<<"stocks 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<<"stocks of shanghai and shenzhen "<
}
----------------------- Page 39-----------------------
第五章:继承与派生类
[5_1]答:类的继承方式有public(公有继承)、protected(保护继承)和privat
e(私有继承)3种,不同的继承方式导致原来具有不同访问属性的基类成员在派
生类中的访问属性也有所不同。
(1)基类中的私有成员
无论哪种继承方式,基类中的私有成员不允许派生类继承,即在派生类中是不可
直接访问的。
(2)基类中的公有成员
当类的继承方式为公有继承时,基类中的所有公有成员在派
生类中仍以公有成员的身份出现;当类的继承方式为私有继承时,基类中的所有
公有成员在派生类中都以私有成员的身份出现;当类的继承方式为保护继承时,
基类中的所有公有成员在派生类中都是以
保护成员的身份出现。
(3)基类中的保护成员
当类的继承方式为公有继承时,基类中的所有保护成员在派
生类中仍以保护成员的身份出现;当类的继承方式为私有继承时,基类中的所有
保护成员在派生类中都是以私有成员的身份出现:当类的继承方式为保护继承
时,基类中的所有保护成员在派生类中仍以保护成员的身份出现。
[5_2]答:派生类不能直接访问基类的私有成员,但是可以通过基类提供的公有
成员函数间接地访问基类的私有成员
[5_3]答:保护成员可以被派生类的成员函数访问,但是对于外界是隐藏起来的,
外部函数不能访问它。因此,为了便于派生类的访问,可以将基类私有成员中需
要提供给派生类访问的成员定义为保护成员。C++规定,派生类对于保护成员的
继承与公有成员的继承很相似,也分为两种情况:若为公有派生,则基类中的保
----------------------- Page 40-----------------------
护成员在派生类中也为保护成员;若为私有派生,则基类中的保护成员在派生类
中成为私有成员。
[5_4]答:通常情况下,当创建派生类对象时,首先执行基类的构造函数,随后
再执行派生类的构造函数;当撤消派生类对象时,则先执行派生类的析构函数函
数,随后再执行基类的析构函数。
[5_5]答:当基类的构造函数没有参数或没有显示定义构造函数时,派生类可以
不向基类传递参数,甚至可以不定义构造函数。当基类含有带参数的构造函数时,
派生类必须定义构造函数,以提供把参数传递给基类构造函数的途径。
派生类构造函数的一般格式如下:
派生类构造函数名(参数表):基类构造函数名(参数表)
{ }
其中基类构造函数的参数,通常来源于派生类构造函数的参数表,也可以用常数
值。由于析构函数是不带参数的,在派生类中是否定义析构函数与它所属的基类
无关,基类的析构函数不会因为派生类没有析构函数而得不到执行,它们各自是
独立的。
[5_6]答:当一个派生类具有多个基类时,这种派生方法称为多继承。多继承构
造函数的执行顺序与单继承构造函数的执行顺序相同,也是遵循先执行基类的构
造函数,再执行对象成员的构造函数,最后执行派生类构造函数的原则。在多个
基类之间则严格按照派生类声明时从左到右的顺序来排列先后。而析构函数的执
行顺序刚好与构造函数的执行顺序相反。
[5_7]在类的派生中为何要引入虚基类?虚基类构造函数的调用顺序是如何规定
的?
当引用派生类的成
员时,首先在派生类自身的作用域中寻找这个成员,如果没有
找到,则到它的基类中寻找。如果一个派生类是从多个基类派生来的,而这些基
----------------------- Page 41-----------------------
类又有一个共同的基类,则在这个派生类中访问这个共同的基类中的成员时,可
能会产生二义性。为了解决这种二义性,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的构造函数的调用被忽略了,这是初始化虚基类和非虚
----------------------- Page 42-----------------------
基类不同的地方。(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<
if((k)%12 0)
cout<<"\n";
k++;
}
cout<<"\n";
}
class der_table:public table
----------------------- Page 43-----------------------
{ protected:
char *c;
public:
der_table(int p,int q,char *m):table(p,q)
{ c=m; }
voi
d print(void);
};
void der_table::print(void)
{ cout<
table::ascii();
}
void main()
{ der_table ob('a','z',"ASCII value---char");
ob.print();
}
[5_16]程序编制如下:
#include
#include
class time
{ public:
time(int h,int m,int s)
{ hours=h;
minutes=m;
seconds=s;
}
----------------------- Page 44-----------------------
virtual void display()
{ cout<
protected:
int hours,minutes,seconds;
};
class date
{ public:
date(int m,int d,int y)
{ month=m;
day=d;
year=y;
}
virtual void display()
{ cout<
protected:
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<
----------------------- Page 45-----------------------
date::display();
cout<
time::display();
}
protected:
char childname[20];
};
void main()
{ birthtime yx("yuanxiang ",10,27,1966,13,20,0);
yx.display();
}
[5_17]程序如下
#include
#include
class animal
{ public:
animal()
{ name=NULL; }
animal(char *n);
~animal()
{ delete name; }
virtual void whoami();
protected:
char *name;
};
----------------------- Page 46-----------------------
class cat:public animal
{ 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<<"generic animal"; }
void cat::whoami()
{ cout<<"i am a cat name "<
void tiger::whoami()
--------------------
--- Page 47-----------------------
{ cout<<"i am atiger name "<
void main() //添加的主函数
{ cat cat("john");
cat.whoami();
tiger tiger("richard");
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)
----------------------- Page 48-----------------------
{ bedrooms=br; bathrooms=bth; }
void show()
{ cout<<"floors: "<
cout<<"rooms: "<
cout<<"square footage: "<
cout<<"bedrooms: "<
cout<<"bathroorns: "<
}
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<<"floors: "<
cout<<"rooms: "<
cout<<"square footage: "<
cout<<"telephones: "<
cout<<"fire extinguishers: ";
cout<
}
private:
int phones;// 电话数
int extinguishers;//灭火器数
----------------------- Page 49-----------------------
};
main()
{ house h_ob(2,12,5000,6,4);
office o_ob(4,25,12000,30,8);
cout<<"housee: \n";
h_ob.show();
cout<<"\noffice: \n";
o_ob.show();
return 0;
}
[5_19]实现本题功能的程序如下:
#include
#include
const int l=80;
class person
{ public:
void input()
{ cout<<"\n input name: ";
cin>>name;
cout<<"\n certificate no: ";
cin>>id;
}
void print()
{ cout<<"\n certificate no: "<
void printname()
{ cout<
private:
char name[l],id[l];
----------------------- Page 50-----------------------
};
class stud
{ public:
void input()
{ cout<<" input address: ";
cin>>addr;
cout<<" input telphone no: ";
cin>>tel;
}
void print()
{ cout<<"\n address: "<<"\t"<
cout<<"\n telphone no: "<<"\t"<
}
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();
----------------------- Page 51-----------------------
cout<<"\n tears old: "<<"\t"<
cout<<"\n score no: "<<"\t"<
}
void printname()
{ person::printname(); }
private:
int old;
unsigned long sno;
};
class score:private student,private stud
{ public:
void input()
{ student::input();
stud::input();
cout<<"input math score: ";
cin>>math;
cout<<"input english score: ";
cin>>eng;
}
void print()
{ student::print();
stud::print();
cout<<"\n math score: "<<"\t"<