admin 管理员组

文章数量: 1086019


2025年1月1日发(作者:oracle数据库如何查询)

第 一 章 概述

1-1 简述计算机程序设计语言的发展历程。

解:迄今为止计算机程序设计语言的发展经历

了机器语言、汇编语言、高级语言等阶段,

C++语言是一种面向对象的编程语言,也属于

高级语言。

1-2 面向对象的编程语言有哪些特点?

解: 面向对象的编程语言与以往各种编程语

言有根本的不同,它设计的出发点就是为了能

更直接的描述客观世界中存在的事物以及它

们之间的关系。面向对象的编程语言将客观事

物看作具有属性和行为的对象,通过抽象找出

同一类对象的共同属性(静态特征)和行为(动

态特征),形成类。通过类的继承与多态可以

很方便地实现代码重用,大大缩短了软件开发

周期,并使得软件风格统一。因此,面向对象

的编程语言使程序能够比较直接地反问题域

的本来面目,软件开发人员能够利用人类认识

事物所采用的一般思维方法来进行软件开发。

C++语言是目前应用最广的面向对象的编程

语言。

1-3 什么是结构化程序设计方法?这种方法

有哪些优点和缺点?

解:结构化程序设计的思路是:自顶向下、逐

步求精;其程序结构是按功能划分为若干个基

本模块;各模块之间的关系尽可能简单,在功

能上相对独立;每一模块内部均是由顺序、选

择和循环三种基本结构组成;其模块化实现的

具体方法是使用子程序。结构化程序设计由于

采用了模块分解与功能抽象,自顶向下、分而

治之的方法,从而有效地将一个较复杂的程序

系统设计任务分解成许多易于控制和处理的

子任务,便于开发和维护。 虽然结构化程序

设计方法具有很多的优点,但它仍是一种面向

过程的程序设计方法,它把数据和处理数据的

过程分离为相互独立的实体。当数据结构改变

时,所有相关的处理过程都要进行相应的修

改,每一种相对于老问题的新方法都要带来额

外的开销,程序的可重用性差。

由于图形用户界面的应用,程序运行由顺序运

行演变为事件驱动,使得软件使用起来越来越

方便,但开发起来却越来越困难,对这种软件

的功能很难用过程来描述和实现,使用面向过

程的方法来开发和维护都将非常困难。 1-4

什么是对象?什么是面向对象方法?这种方

法有哪些特点?

解:从一般意义上讲,对象是现实世界中一个

实际存在的事物,它可以是有形的,也可以是

无形的。对象是构成世界的一个独立单位,它

具有自己的静态特征和动态特征。面向对象方

法中的对象,是系统中用来描述客观事物的一

个实体,它是用来构成系统的一个基本单位,

由一组属性和一组行为构成。

面向对象的方法将数据及对数据的操作方法

放在一起,作为一个相互依存、不可分离的整

体--对象。对同类型对象抽象出其共性,形成

类。类中的大多数数据,只能用本类的方法进

行处理。类通过一个简单的外部接口,与外界

发生关系,对象与对象之间通过消息进行通

讯。这样,程序模块间的关系更为简单,程序

模块的独立性、数据的安全性就有了良好的保

障。通过实现继承与多态性,还可以大大提高

程序的可重用性,使得软件的开发和维护都更

为方便。

面向对象方法所强调的基本原则,就是直接面

对客观存在的事物来进行软件开发,将人们在

日常生活中习惯的思维方式和表达方式应用

在软件开发中,使软件开发从过分专业化的方

法、规则和技巧中回到客观世界,回到人们通

常的思维。

1-5 什么叫做封装?

解: 封装是面向对象方法的一个重要原则,

就是把对象的属性和服务结合成一个独立的

系统单位,并尽可能隐蔽对象的内部细节。

1-6 面向对象的软件工程包括哪些主要内

容?

解:面向对象的软件工程是面向对象方法在软

件工程领域的全面应用,它包括面向对象的分

析(OOA)、面向对象的设计(OOD)、面向

对象的编程(OOP)、面向对象的测试(OOT)

和面向对象的软件维护(OOSM)等主要内容。

1-7 简述计算机内部的信息可分为几类?

解:计算机内部的信息可以分成控制信息和数

据信息二大类;控制信息可分为指令和控制字

两类;数据信息可分为数值信息和非数值信息

两类。 1-8 什么叫二进制?使用二进制有何

优点和缺点?

解:二进制是基数为2,每位的权是以2 为底

的幂的进制,遵循逢二进一原则,基本符号为

0和1。采用二进制码表示信息,有如下几个

优点:1.易于物理实现;2.二进制数运算简单;

3.机器可靠性高;4.通用性强。其缺点是它表

示数的容量较小,表示同一个数,二进制较其

他进制需要更多的位数。

1-9 请将以下十进制数值转换为二进制和十

六进制补码:

(1)2 (2)9 (3)93 (4)-32 (5)65535

(6)-1 解:(1) (2)10 = (10)2 = (2)

16

(2) (9)10 = (1001)2 = (9)16

(3) (93)10 = (1011101)2 = (5D)16

(4) (-32)10 = (11100000)2 = (E0)

16

(5) (65535)10 = (11111111 11111111)

2 = (FFFF)16

(6) (-1)10 = (11111111 11111111)2 =

(FFFF)16

1-10 请将以下数值转换为十进制:

(1)(1010)2 (2)(10001111)2 (3)(01011111

11000011)2 (4)(7F)16 (5)(2D3E)16

(6)(F10E)16

解: (1)(1010)2 = (10)10

(2)(10001111)2 = (143)10

(3)(01011111 11000011)2 = (24515)10

(4)(7F)16 = (127)10

(5)(2D3E)16 = (11582)10

(6)(F10E)16 = (61710)10

1-11 简要比较原码、反码、补码等几种编码

方法。

解:原码:将符号位数字化为 0 或 1,数的

绝对值与符号一起编码,即所谓"符号──绝

对值表示"的编码。

正数的反码和补码与原码表示相同。

负数的反码与原码有如下关系:

符号位相同(仍用1表示),其余各位取反(0变

1,1变0)。

补码由该数反码的最末位加1求得。

第 二

C++简单程序设计

2-1 C++语言有那些主要特点和优点?

解: C++语言的主要特点表现在两个方面,

一是全面兼容C,二是支持面向对象的方法。

C++是一个更好的C,它保持了C的简洁、高

效、接近汇编语言、具有良好的可读性和可移

植性等特点,对C的类型系统进行了改革和

扩充,因此C++比C更安全,C++的编译系统

能检查出更多的类型错误。 C++语言最重要

的特点是支持面向对象。

2-2 下列标识符哪些是合法的?

Program, -page, _lock, test2, 3in1,

@mail, A_B_C_D

解: Program, _lock, test2, A_B_C_D

是合法的标识符,其它的不是。

2-3 例2.1中每条语句的作用是什么?

#include

void main(void)

{

cout<<"Hello!n";

cout<<"Welcome to c++!n";

}

解: #include //指示编译器将文

件iostream.h中的代码

//嵌入到该程序中该指令所在的地方

void main() //主函数名,void 表示函数没有返

回值

{ //函数体标志

cout<<"Hello!n"; //输出字符串Hello!到标准

输出设备(显示器)上。

cout<<"Welcome to c++!n"; //输出字符串

Welcome to c++!

}

在屏幕输出如下:

Hello!

Welcome to c++!

2-4 使用关键字const而不是#define语句的好

处有哪些?

解: const定义的常量是有类型的,所以在使

用它们时编译器可以查错;而且,这些变量在

调试时仍然是可见的。

2-5 请写出C++语句声明一个常量PI,值为

3.1416;再声明一个浮点型变量a,把PI的值

赋给a。

解: const float PI = 3.1416;

float a = PI;

2-6 在下面的枚举类型中,Blue的值是多少?

enum COLOR { WHITE, BLACK = 100,

RED, BLUE, GREEN = 300 };

解: Blue = 102

2-7 注释有什么作用?C++中有哪几种注释

的方法?他们之间有什么区别?

解: 注释在程序中的作用是对程序进行注解

和说明,以便于阅读。编译系统在对源程序进

行编译时不理会注释部分,因此注释对于程序

的功能实现不起任何作用。而且由于编译时忽

略注释部分,所以注释内容不会增加最终产生

的可执行程序的大小。适当地使用注释,能够

提高程序的可读性。在C++中,有两种给出

注释的方法:一种是延用C语言方法,使用

"/*"和"*/"括起注释文字。另一种方法是使用

"//",从"//"开始,直到它所在行的行尾,所有

字符都被作为注释处理。

2-8 什么叫做表达式?x = 5 + 7是一个表达

式吗?它的值是多少?

解: 任何一个用于计算值的公式都可称为表

达式。x = 5 + 7是一个表达式,它的值为12。

2-9 下列表达式的值是多少?

1. 201 / 4

2. 201 % 4

3. 201 / 4.0

解: 1. 50

2. 1

3. 50.25

2-10 执行完下列语句后,a、b、c三个变量的

值为多少?

a = 30;

b = a++;

c = ++a;

解: a:32 ; b:30 ; c:32;

2-11 在一个for循环中,可以初始化多个变量

吗?如何实现?

解: 在for循环设置条件的第一个";"前,用,

分隔不同的赋值表达式。

例如:

for (x = 0, y = 10; x < 100; x++, y++)

2-12 执行完下列语句后,n的值为多少?

int n;

for (n = 0; n < 100; n++)

解: n的值为100

2-13 写一条for语句,计数条件为n从100

到200,步长为2;然后用while和do…while

语句完成同样的循环。

解: for循环:

for (int n = 100; n <= 200; n += 2); while循环:

int x = 100;

while (n <= 200)

n += 2; do…while循环:

int n = 100;

do

{

n += 2;

} while(n <= 200);

2-14 if ( x = 3 ) 和 if (x = = 3) 这两条语句的

差别是什么?

解: 语句if(x = 3)把3赋给x,赋值表达式的

值为true,作为if语句的条件;语句if(x == 3)

首先判断x的值是否为3,若相等条件表达式

的值为ture,否则为false。

2-15 什么叫做作用域?什么叫做局部变量?

什么叫做全局变量,如何使用全局变量? 解:

作用域是一个标识符在程序正文中有效的区

域。局部变量,一般来讲就是具有块作用域的

变量;全局变量,就是具有文件作用域的变量。

2-16 已知x、y两个变量,写一条简单的if

语句,把较小的的值赋给原本值较大的变量。

解: if (x > y)

x = y;

else // y > x || y == x

y = x;

2-17 修改下面这个程序中的错误,改正后它

的运行结果是什么?

#include

void main()

int i

int j;

i = 10; /* 给i赋值

j = 20; /* 给j赋值 */

cout << "i + j = << i + j; /* 输出结果 */

return 0;

}

解: 改正:

#include

int main()

{

int i;

int j;

i = 10; // 给i赋值

j = 20; /* 给j赋值 */

cout << "i + j = " << i + j; /* 输出结果 */

return 0;

}

程序运行输出:

i + j = 30

2-18 编写一个程序,运行时提示输入一个数

字,再把这个数字显示出来。

解: 源程序:

#include int main()

{

int i;

cout << "请输入一个数字:";

cin >> i;

cout << "您输入一个数字是" << i << endl;

return 0;

}

程序运行输出:

请输入一个数字:5

您输入一个数字是5

2-19 C++有哪几种数据类型?简述其值域。编

程显示你使用的计算机中的各种数据类型的

字节数。

解: 源程序:

#include int main()

{

cout << "The size of an int is:tt" << sizeof(int)

<< " bytes.n";

cout << "The size of a short int is:t" <<

sizeof(short) << " bytes.n";

cout << "The size of a long int is:t" <<

sizeof(long) << " bytes.n";

cout << "The size of a char is:tt" <<

sizeof(char) << " bytes.n";

cout << "The size of a float is:tt" <<

sizeof(float) << " bytes.n";

cout << "The size of a double is:t" <<

sizeof(double) << " bytes.n";

return 0;

}

程序运行输出:

The size of an int is: 4 bytes.

The size of a short int is: 2 bytes.

The size of a long int is: 4 bytes.

The size of a char is: 1 bytes.

The size of a float is: 4 bytes.

The size of a double is: 8 bytes.

2-20 打印ASCII码为32~127的字符。

解: #include

int main()

{

for (int i = 32; i<128; i++)

cout << (char) i;

return 0;

}

程序运行输出:

!"#$%G'()*+,./:;<>?@ABCDEFG

HIJKLMNOP_QRSTUVWXYZ[]^'abcdefghijk

lmnopqrstuvwxyz<|>~s

2-21 运行下面的程序,观察其输出,与你的

设想是否相同?

#include

int main()

{

unsigned int x;

unsigned int y = 100;

unsigned int z = 50;

x= y - z;

cout << "Difference is: " << x;

x = z - y;

cout << "nNow difference is: " << x <

return 0;

}

解: 程序运行输出:

Difference is: 50

Now difference is: 4294967246

注意,第二行的输出并非 -50,注意x、y、z

的数据类型。

2-22 运行下面的程序,观察其输出,体会i++

与++i的差别。

#include

int main()

{

int myAge = 39; // initialize two integers

int yourAge = 39;

cout << "I am: " << myAge << " years old.n";

cout << "You are: " << yourAge << " years

oldn";

myAge++; // postfix increment

++yourAge; // prefix increment

cout << "One ";

cout << "I am: " << myAge << " years old.n";

cout << "You are: " << yourAge << " years

oldn";

cout << "Another year passesn";

cout << "I am: " << myAge++ << " years

old.n";

cout << "You are: " << ++yourAge << " years

oldn";

cout << "Let's print it again.n";

cout << "I am: " << myAge << " years old.n";

cout << "You are: " << yourAge << " years

oldn";

return 0;

}

解: 程序运行输出:

I am 39 years old

You are 39 years old

One year passes

I am 40 years old

You are 40 years old

Another year passes

I am 40 years old

You are 41 years old

Let's print it again

I am 41 years old

You are 41 years old

2-23 什么叫常量?什么叫变量?

解: 所谓常量是指在程序运行的整个过程中

其值始终不可改变的量,除了用文字表示常量

外,也可以为常量命名,这就是符号常量;在

程序的执行过程中其值可以变化的量称为变

量,变量是需要用名字来标识的。

2-24 变量有哪几种存储类型?

解: 变量有以下几种存储类型:

auto存储类型:采用堆栈方式分配内存空间,

属于一时性存储,其存储空间可以被若干变量

多次覆盖使用; register存储类型:存放在通

用寄存器中;

extern存储类型:在所有函数和程序段中都可

引用;

static存储类型:在内存中是以固定地址存放

的,在整个程序运行期间都有效。

2-25 写出下列表达式的值:

1. 2 < 3 && 6 < 9

2. ! ( 4<7 )

3. ! ( 3 > 5) || (6 < 2 )

解: 1. true

2. false

3. true

2-26 若a = 1,b = 2,c = 3,下列各式的结果

是什么?

1. a | b - c

2. a ^ b & -c

3. a & b | c

4. a | b & c

解: 1. -1

2. 1

3. 3

4. 3

2-27 若a = 1,下列各式的结果是什么?

1. ! a | a

2. ~ a | a

3. a ^ a

4. a >> 2

解: 1. 1

2. -1

3. 0

4. 0

2-28 编写一个完整的程序,实现功能:向用

户提问"现在正在下雨吗?",提示用户输入Y

或N。若输入为Y,显示"现在正在下雨。";

若输入为N,显示"现在没有下雨。";否则继

续提问"现在正在下雨吗?"

解: 源程序:

#include

#include void main()

{

char flag;

while(1)

{

cout << "现在正在下雨吗?(Yes or No):";

cin >> flag;

if ( toupper(flag) == 'Y')

{

cout << "现在正在下雨。";

break;

}

if ( toupper(flag) == 'N')

{

cout << "现在没有下雨。";

break;

}

}

}

程序运行输出:

现在正在下雨吗?(Yes or No):x

现在正在下雨吗?(Yes or No):l

现在正在下雨吗?(Yes or No):q

现在正在下雨吗?(Yes or No):n

现在没有下雨。

或:

现在正在下雨吗?(Yes or No):y

现在正在下雨。

2-29 编写一个完整的程序,运行时向用户提

问"你考试考了多少分?(0~100)",接收输

入后判断其等级,显示出来。规则如下:

解: #include void main()

{

int i,score;

cout << "你考试考了多少分?(0~100):";

cin >> score;

if (score>100 || score<0)

cout << "分数值必须在0到100之间!";

else

{

i = score/10;

switch (i)

{

case 10:

case 9:

cout << "你的成绩为优!";

break;

case 8:

cout << "你的成绩为良!";

break;

case 7:

case 6:

cout << "你的成绩为中!";

break;

default:

cout << "你的成绩为差!";

}

} }

程序运行输出:

你考试考了多少分?(0~100):85

你的成绩为良!

2-30 (1)实现一个简单的菜单程序,运行时

显示"Menu: A(dd) D(elete) S(ort) Q(uit),

Select one:"提示用户输入,A表示增加,D表

示删除,S表示排序,Q表示退出,输入为A、

D、S时分别提示"数据已经增加、删除、排序。

"输入为Q时程序结束。要求使用if … else

语句进行判断,用break、continue控制程序

流程。

解: #include

#include void main()

{

char choice,c;

while(1)

{

cout << "Menu: A(dd) D(elete) S(ort) Q(uit),

Select one:";

cin >> c;

choice = toupper(c);

if (choice == 'A')

{

cout << "数据已经增加. " << endl;

continue;

}

else if (choice == 'D')

{

cout << "数据已经删除. " << endl;

continue;

}

else if (choice == 'S')

{

cout << "数据已经排序. " << endl;

continue;

}

else if (choice == 'Q')

break;

}

}

程序运行输出:

Menu: A(dd) D(elete) S(ort) Q(uit), Select

one:a

数据已经增加. Menu: A(dd) D(elete) S(ort)

Q(uit), Select one:d

数据已经删除. Menu: A(dd) D(elete) S(ort)

Q(uit), Select one:s

数据已经排序. Menu: A(dd) D(elete) S(ort)

Q(uit), Select one:q (2)实现一个简单的菜

单程序,运行时显示"Menu: A(dd) D(elete)

S(ort) Q(uit), Select one:"提示用户输入,A

表示增加,D表示删除,S表示排序,Q表示

退出,输入为A、D、S时分别提示"数据已经

增加、删除、排序。"输入为Q时程序结束。

要求使用Switch语句。

解: 源程序:

#include

#include void main()

{

char choice;

while(1)

{

cout << "Menu: A(dd) D(elete) S(ort) Q(uit),

Select one:";

cin >> choice;

switch(toupper(choice))

{

case 'A':

cout << "数据已经增加. " << endl;

break;

case 'D':

cout << "数据已经删除. " << endl;

break;

case 'S':

cout << "数据已经排序. " << endl;

break;

case 'Q':

exit(0);

break;

default:

;

}

}

}

程序运行输出:

Menu: A(dd) D(elete) S(ort) Q(uit), Select

one:a

数据已经增加. Menu: A(dd) D(elete) S(ort)

Q(uit), Select one:d

数据已经删除. Menu: A(dd) D(elete) S(ort)

Q(uit), Select one:s

数据已经排序. Menu: A(dd) D(elete) S(ort)

Q(uit), Select one:q

2-31 用穷举法找出1~100间的质数,显示出

来。分别使用while,do-while,for循环语句

实现。

解: 源程序: 使用while循环语句:

#include

#include void main()

{

int i,j,k,flag;

i = 2;

while(i <= 100)

{

flag = 1;

k = sqrt(i);

j = 2;

while (j <= k)

{

if(i%j == 0)

{

flag = 0;

break;

}

j++;

}

if (flag)

cout << i << "是质数." << endl;

i++;

}

} 使用do…while循环语句:

#include

#include void main()

{

int i,j,k,flag;

i = 2;

do{

flag = 1;

k = sqrt(i);

j = 2;

do{

if(i%j == 0)

{

flag = 0;

break;

}

j++;

}while (j <= k);

if (flag)

cout << i << "是质数." << endl;

i++;

}while(i <= 100);

} 使用for循环语句:

#include

#include void main()

{

int i,j,k,flag;

for(i = 2; i <= 100; i++)

{

flag = 1;

k = sqrt(i);

for (j = 2; j <= k; j++)

{

if(i%j == 0)

{

flag = 0;

break;

}

}

if (flag)

cout << i << "是质数." << endl;

}

}

程序运行输出:

2是质数.

3是质数.

5是质数.

7是质数.

11是质数.

13是质数.

17是质数.

19是质数.

23是质数.

29是质数.

31是质数.

37是质数.

41是质数.

43是质数.

47是质数.

53是质数.

59是质数.

61是质数.

67是质数.

71是质数.

73是质数.

79是质数.

83是质数.

89是质数.

97是质数.

2-32 比较Break语句与Continue语句的不同

用法。

解: Break使程序从循环体和switch语句内

跳出,继续执行逻辑上的下一条语句,不能用

在别处;

continue 语句结束本次循环,接着开始判断决

定是否继续执行下一次循环; 2-33 定义一

个表示时间的结构体,可以精确表示年、月、

日、小时、分、秒;提示用户输入年、月、日、

小时、分、秒的值,然后完整地显示出来。 解:

源程序见"实验指导"部分实验二

2-34 在程序中定义一个整型变量,赋以1~100

的值,要求用户猜这个数,比较两个数的大小,

把结果提示给用户,直到猜对为止。分别使用

while、do…while语句实现循环。

解: //使用while语句

#include void main() {

int n = 18;

int m = 0;

while(m != n) {

cout << "请猜这个数的值为多少?(0~~100):";

cin >> m;

if (n > m)

cout << "你猜的值太小了!" << endl;

else if (n < m)

cout << "你猜的值太大了!" << endl;

else

cout << "你猜对了!" << endl;

}

}

//使用do…while语句

#include void main() {

int n = 18;

int m = 0;

do{

cout << "请猜这个数的值为多少?(0~~100):";

cin >> m;

if (n > m)

cout << "你猜的值太小了!" << endl;

else if (n < m)

cout << "你猜的值太大了!" << endl;

else

cout << "你猜对了!" << endl;

}while(n != m);

} 程序运行输出:

请猜这个数的值为多少?(0~~100):50

你猜的值太大了!

请猜这个数的值为多少?(0~~100):25

你猜的值太大了! 请猜这个数的值为多少?

(0~~100):10

你猜的值太小了!

请猜这个数的值为多少?(0~~100):15

你猜的值太小了!

请猜这个数的值为多少?(0~~100):18

你猜对了! 2-35 定义枚举类型weekday,

包括Sunday到Saturday七个元素在程序中定

义weekday类型的变量,对其赋值,定义整

型变量,看看能否对其赋weekday类型的值。

解: #include enum weekday

{

Sunday,

Monday,

Tuesday,

Wednesday,

Thursday,

Friday,

Saturday

}; void main()

{

int i;

weekday d = Thursday;

cout << "d = " << d << endl;

i = d;

cout << "i = " << i << endl;

d = (weekday)6;

cout << "d = " << d << endl;

d = weekday( 4 );

cout << "d = " << d << endl;

} 程序运行输出:

d = 4

i = 4

d = 6

d = 4

第三章

函数

3-1 C++中的函数是什么?什么叫主调函数,

什么叫被调函数,二者之间有什么关系?如何

调用一个函数? 解: 一个较为复杂的系统往

往需要划分为若干子系统,高级语言中的子程

序就是用来实现这种模块划分的。C和C++

语言中的子程序就体现为函数。调用其它函数

的函数被称为主调函数,被其它函数调用的函

数称为被调函数。一个函数很可能既调用别的

函数又被另外的函数调用,这样它可能在某一

个调用与被调用关系中充当主调函数,而在另

一个调用与被调用关系中充当被调函数。

调用函数之前先要声明函数原型。按如下形式

声明:

类型标识符 被调函数名 (含类型说明的形参

表);

声明了函数原型之后,便可以按如下形式调用

子函数:

函数名(实参列表)

3-2 观察下面程序的运行输出,与你设想的有

何不同?仔细体会引用的用法。

源程序:

#include int main()

{

int intOne;

int &rSomeRef = intOne;

intOne = 5;

cout << "intOne:tt" << intOne << endl;

cout << "rSomeRef:t" << rSomeRef << endl;

int intTwo = 8;

rSomeRef = intTwo; // not what you think!

cout << "nintOne:tt" << intOne << endl;

cout << "intTwo:tt" << intTwo << endl;

cout << "rSomeRef:t" << rSomeRef << endl;

return 0;

}

程序运行输出:

intOne: 5

rSomeRef: 5

intOne: 8

intTwo: 8

rSomeRef: 8

3-3 比较值调用和引用调用的相同点与不同

点。

解: 值调用是指当发生函数调用时,给形参

分配内存空间,并用实参来初始化形参(直接

将实参的值传递给形参)。这一过程是参数值

的单向传递过程,一旦形参获得了值便与实参

脱离关系,此后无论形参发生了怎样的改变,

都不会影响到实参。

引用调用将引用作为形参,在执行主调函数中

的调用语句时,系统自动用实参来初始化形

参。这样形参就成为实参的一个别名,对形参

的任何操作也就直接作用于实参。

3-4 什么叫内联函数?它有哪些特点?

解: 定义时使用关键字 inline的函数叫做内

联函数;

编译器在编译时在调用处用函数体进行替换,

节省了参数传递、控制转移等开销;

内联函数体内不能有循环语句和switch语句;

内联函数的定义必须出现在内联函数第一次

被调用之前;

对内联函数不能进行异常接口声明;

3-5 函数原型中的参数名与函数定义中的参

数名以及函数调用中的参数名必须一致吗?

解: 不必一致,所有的参数是根据位置和类

型而不是名字来区分的。 3-6 重载函数时通

过什么来区分?

解: 重载的函数的函数名是相同的,但它们

的参数的个数和数据类型不同,编译器根据实

参和形参的类型及个数的最佳匹配,自动确定

调用哪一个函数。

3-7 编写函数,参数为两个unsigned short int

型数,返回值为第一个参数除以第二个参数的

结果,数据类型为short int;如果第二个参数

为0,则返回值为-1。在主程序中实现输入输

出。

解: 源程序:

#include

short int Divider(unsigned short int a, unsigned

short int b)

{

if (b == 0)

return -1;

else

return a/b;

}

typedef unsigned short int USHORT;

typedef unsigned long int ULONG;

int main()

{

USHORT one, two;

short int answer;

cout << "Enter two numbers.n Number one: ";

cin >> one;

cout << "Number two: ";

cin >> two;

answer = Divider(one, two);

if (answer > -1)

cout << "Answer: " << answer;

else

cout << "Error, can't divide by zero!";

return 0;

}

程序运行输出:

Enter two numbers.

Number one:8

Number two:2

Answer: 4

3-8 编写函数把华氏温度转换为摄氏温度,公

式为:C = (F - 32) * 5/9; 在主程序中提示用户

输入一个华氏温度,转化后输出相应的摄氏温

度。

解: 源程序见"实验指导"部分实验三

3-9 编写函数判断一个数是否是质数,在主程

序中实现输入、输出。

解: #include

#include

int prime(int i); //判一个数是否是质数的函数

void main()

{

int i;

cout << "请输入一个整数:";

cin >> i;

if (prime(i))

cout << i << "是质数." << endl;

else

cout << i << "不是质数." << endl;

}

int prime(int i)

{

int j,k,flag;

flag = 1;

k = sqrt(i);

for (j = 2; j <= k; j++)

{

if(i%j == 0)

{

flag = 0;

break;

}

}

if (flag)

return 1;

else

return 0;

}

程序运行输出:

请输入一个整数:1151

1151是质数.

3-10 编写函数求两个整数的最大公约数和最

小公倍数。

解: 源程序:

#include

#include

int fn1(int i,int j); //求最大公约数的函数

void main()

{

int i,j,x,y;

cout << "请输入一个正整数:";

cin >> i ;

cout << "请输入另一个正整数:";

cin >> j ;

x = fn1(i,j);

y = i * j / x;

cout << i << "和" << j << "的最大公约数是:"

<< x << endl;

cout << i << "和" << j << "的最小公倍数是:"

<< y << endl;

}

int fn1(int i, int j)

{

int temp;

if (i < j)

{

temp = i;

i = j;

j = i;

}

while(j != 0)

{

temp = i % j;

i = j;

j = temp;

}

return i;

}

程序运行输出:

请输入一个正整数:120

请输入另一个正整数:72

120和72的最大公约数是:24

120和72的最小公倍数是:360

3-11 什么叫作嵌套调用?什么叫作递归调

用?

解: 函数允许嵌套调用,如果函数1调用了

函数2,函数2再调用函数3,便形成了函数

的嵌套调用。

函数可以直接或间接地调用自身,称为递归调

用。

3-12 在主程序中提示输入整数n,编写函数

用递归的方法求1 + 2 + … + n的值。

解: #include

#include

int fn1(int i);

void main()

{

int i;

cout << "请输入一个正整数:";

cin >> i ;

cout << "从1累加到" <

fn1(i) << endl;

}

int fn1(int i)

{

if (i == 1)

return 1;

else

return i + fn1(i -1);

}

程序运行输出: 请输入一个正整数:100

从1累加到100的和为:5050

3-13 编写递归函数GetPower(int x, int y)计

算x的y次幂, 在主程序中实现输入输出。

解: 源程序:

#include

long GetPower(int x, int y);

int main()

{

int number, power;

long answer;

cout << "Enter a number: ";

cin >> number;

cout << "To what power? ";

cin >> power;

answer = GetPower(number,power);

cout << number << " to the " << power << "th

power is " <

return 0;

}

long GetPower(int x, int y)

{

if(y == 1)

return x;

else

return (x * GetPower(x,y-1));

}

程序运行输出:

Enter a number: 3

To what power? 4

3 to the 4th power is 81

3-14 用递归的方法编写函数求Fibonacci 级

数,公式为fib(n) = fib(n-1) + fib(n-2),n>2;

fib(1) = fib(2) = 1;观察递归调用的过程。

解: 源程序见"实验指导"部分实验三

3-15 用递归的方法编写函数求n阶勒让德多

项式的值,在主程序中实现输入、输出;

解: #include

float p(int n, int x);

void main()

{

int n,x;

cout << "请输入正整数n:";

cin >> n;

cout << "请输入正整数x:";

cin >> x;

cout << "n = " << n << endl;

cout << "x = " << x << endl;

cout << "P" << n << "(" << x << ") = " << p(n,x)

<< endl;

}

float p(int n, int x)

{

if (n == 0)

return 1;

else if (n == 1)

return x;

else

return ((2*n-1)*x*p(n-1,x) - (n-1)*p(n-2,x)) /n ;

}

程序运行输出:

请输入正整数n:1

请输入正整数x:2

n = 1

x = 2

P1(2) = 2

请输入正整数n:3

请输入正整数x:4

n = 3

x = 4

P3(4) = 154

3-16 使用模板函数实现Swap( x, y ),函数

功能为交换x、y的值。

解: 源程序:

#include

template void swap(T &x, T

&y)

{

T z;

z = x;

x = y;

y = z;

}

void main()

{

int j = 1, k = 2;

double v = 3.0, w = 4.0;

cout << "j = " <cout << "v = " <swap(j, k); //int

swap(v, w); //double

cout << "After swap:" << endl;

cout << "j = " <cout << "v = " <}

程序运行输出:

j = 1 k = 2

v = 3.14 w = 4.35

After swap:

j = 2 k = 1

v = 4.35 w = 3.14

第四章 类

4-1 解释public和private的作用,公有类型

成员与私有类型成员有些什么区别?

解: 公有类型成员用public关键字声明,公

有类型定义了类的外部接口;私有类型的成员

用private关键字声明,只允许本类的函数成

员来访问,而类外部的任何访问都是非法的,

这样,私有的成员就整个隐蔽在类中,在类的

外部根本就无法看到,实现了访问权限的有效

控制。

4-2 protected关键字有何作用?

解: protected用来声明保护类型的成员,保

护类型的性质和私有类型的性质相似,其差别

在于继承和派生时派生类的成员函数可以访

问基类的保护成员。

4-3 构造函数和析构函数有什么作用?

解: 构造函数的作用就是在对象被创建时利

用特定的值构造对象,将对象初始化为一个特

定的状态,使此对象具有区别于彼对象的特

征,完成的就是是一个从一般到具体的过程,

构造函数在对象创建的时候由系统自动调用。

析构函数与构造函数的作用几乎正好相反,它

是用来完成对象被删除前的一些清理工作,也

就是专门作扫尾工作的。一般情况下,析构函

数是在对象的生存期即将结束的时刻由系统

自动调用的,它的调用完成之后,对象也就消

失了,相应的内存空间也被释放。

4-4 数据成员可以为公有的吗?成员函数可

以为私有的吗?

解: 可以,二者都是合法的。数据成员和成

员函数都可以为公有或私有的。但数据成员最

好定义为私有的。

4-5 已知class A中有数据成员int a,如果定

义了A的两个对象A1、A2,它们各自的数据

成员a的值可以不同吗?

解: 可以,类的每一个对象都有自己的数据

成员。 4-6 什么叫做拷贝构造函数?拷贝构

造函数何时被调用?

解: 拷贝构造函数是一种特殊的构造函数,

具有一般构造函数的所有特性,其形参是本类

的对象的引用,其作用是使用一个已经存在的

对象,去初始化一个新的同类的对象。在以下

三种情况下会被调用:在当用类的一个对象去

初始化该类的另一个对象时;如果函数的形参

是类对象,调用函数进行形参和实参结合时;

如果函数的返回值是类对象,函数调用完成返

回时;

4-7 拷贝构造函数与赋值运算符(=)有何不

同?

解: 赋值运算符(=)作用于一个已存在的对

象;而拷贝构造函数会创建一个新的对象。

4-8 定义一个Dog 类,包含的age、weight

等属性,以及对这些属性操作的方法。实现并

测试这个类。

解: 源程序:

#include

class Dog

{

public:

Dog (int initialAge = 0, int initialWeight = 5);

~Dog();

int GetAge() { return itsAge;} // inline!

void SetAge (int age) { itsAge = age;} // inline!

int GetWeight() { return itsWeight;} // inline!

void SetWeight (int weight) { itsAge = weight;}

// inline!

private:

int itsAge, itsWeight;

};

Dog::Dog(int initialAge, int initialWeight)

{

itsAge = initialAge;

itsWeight = initialWeight;

}

Dog::~Dog() //destructor, takes no action

{

}

int main()

{

Dog Jack(2,10);

cout << "Jack is a Dog who is " ;

cout << () << " years old and";

cout << ght() << " pounds

weight.n";

(7);

ght(20);

cout << "Now Jack is " ;

cout << () << " years old and";

cout << ght() << " pounds weight.";

return 0;

}

程序运行输出:

Jack is a Dog who is 2 years old and 10 pounds

weight.

Now Jack is 7 years old 20 pounds weight.

4-9 设计并测试一个名为Rectangle的矩形

类,其属性为矩形的左下角与右上角两个点的

坐标,能计算矩形的面积。

解: 源程序:

#include

class Rectangle

{

public:

Rectangle (int top, int left, int bottom, int right);

~Rectangle () {}

int GetTop() const { return itsTop; }

int GetLeft() const { return itsLeft; }

int GetBottom() const { return itsBottom; }

int GetRight() const { return itsRight; }

void SetTop(int top) { itsTop = top; }

void SetLeft (int left) { itsLeft = left; }

void SetBottom (int bottom) { itsBottom =

bottom; }

void SetRight (int right) { itsRight = right; }

int GetArea() const;

private:

int itsTop;

int itsLeft;

int itsBottom;

int itsRight;

};

Rectangle::Rectangle(int top, int left, int bottom,

int right)

{

itsTop = top;

itsLeft = left;

itsBottom = bottom;

itsRight = right;

}

int Rectangle::GetArea() const

{

int Width = itsRight-itsLeft;

int Height = itsTop - itsBottom;

return (Width * Height);

}

int main()

{

Rectangle MyRectangle (100, 20, 50, 80 );

int Area = a();

cout << "Area: " << Area << "n";

return 0;

}

程序运行输出:

Area: 3000

Upper Left X Coordinate: 20

4-10 设计一个用于人事管理的People(人员)

类。考虑到通用性,这里只抽象出所有类型人

员都具有的属性:number(编号)、sex(性别)、

birthday(出生日期)、id(身份证号)等等。

其中"出生日期"定义为一个"日期"类内嵌子

对象。用成员函数实现对人员信息的录入和显

示。要求包括:构造函数和析构函数、拷贝构

造函数、内联成员函数、带缺省形参值的成员

函数、聚集。

解: 本题用作实验四的选做题,因此不给出

答案。

4-11 定义一个矩形类,有长、宽两个属性,

有成员函数计算矩形的面积 解: #include

class Rectangle

{

public:

Rectangle(float len, float width)

{

Length = len;

Width = width;

}

~Rectangle(){};

float GetArea() { return Length * Width; }

float GetLength() { return Length; }

float GetWidth() { return Width; }

private:

float Length;

float Width;

};

void main()

{

float length, width;

cout << "请输入矩形的长度:";

cin >> length;

cout << "请输入矩形的宽度:";

cin >> width;

Rectangle r(length, width);

cout << "长为" << length << "宽为" << width

<< "的矩形的面积为:" << a () <<

endl;

}

程序运行输出:

请输入矩形的长度:5

请输入矩形的宽度:4

长为5宽为4的矩形的面积为:20

4-12 定义一个"数据类型" datatype类,能处

理包含字符型、整型、浮点型三种类型的数据,

给出其构造函数。

解: #include

class datatype{

enum{

character,

integer,

floating_point

} vartype;

union {

char c;

int i;

float f;

};

public:

datatype(char ch) {

vartype = character;

c = ch;

}

datatype(int ii) {

vartype = integer;

i = ii;

}

datatype(float ff) {

vartype = floating_point;

f = ff;

}

void print();

};

void datatype::print() {

switch (vartype) {

case character:

cout << "字符型: " << c << endl;

break;

case integer:

cout << "整型: " << i << endl;

break;

case floating_point:

cout << "浮点型: " << f << endl;

break;

}

}

void main() {

datatype A('c'), B(12), C(1.44F);

();

();

();

}

程序运行输出:

字符型: c

整型: 12

浮点型: 1.44

4-13 定义一个Circle类,有数据成员半径

Radius,成员函数GetArea(),计算圆的面积,

构造一个Circle的对象进行测试。

解: #include

class Circle

{

public:

Circle(float radius){ Radius = radius;}

~Circle(){}

float GetArea() { return 3.14 * Radius *

Radius; }

private:

float Radius;

};

void main()

{

float radius;

cout << "请输入圆的半径:";

cin >> radius;

Circle p(radius);

cout << "半径为" << radius << "的圆的面积

为:" << a () << endl;

}

程序运行输出:

请输入圆的半径:5

半径为5的圆的面积为:78.5

4-14 定义一个tree类,有成员ages,成员函

数grow(int years)对ages加上years,age()显示

tree对象的ages的值。

解: #include

class Tree {

int ages;

public:

Tree(int n=0);

~Tree();

void grow(int years);

void age();

};

Tree::Tree(int n) {

ages = n;

}

Tree::~Tree() {

age();

}

void Tree::grow(int years) {

ages += years;

}

void Tree::age() {

cout << "这棵树的年龄为" << ages << endl;

}

void main()

{

Tree t(12);

();

(4);

}

程序运行输出:

这棵树的年龄为12

这棵树的年龄为16

第五C++程序的基本结构

5-1 什么叫做作用域?有哪几种类型的作用

域?

解: 作用域讨论的是标识符的有效范围,作

用域是一个标识符在程序正文中有效的区域。

C++的作用域分为函数原形作用域、块作用域

(局部作用域)、类作用域和文件作用域.

5-2 什么叫做可见性?可见性的一般规则是

什么?

解: 可见性是标识符是否可以引用的问题;

可见性的一般规则是:标识符要声明在前,引

用在后,在同一作用域中,不能声明同名的标

识符。对于在不同的作用域声明的标识符,遵

循的原则是:若有两个或多个具有包含关系的

作用域,外层声明的标识符如果在内层没有声

明同名标识符时仍可见,如果内层声明了同名

标识符则外层标识符不可见。 5-3 下面的程

序的运行结果是什么,实际运行一下,看看与

你的设想有何不同。

#include

void myFunction(); int x = 5, y = 7; int main()

{

cout << "x from main: " << x << "n";

cout << "y from main: " << y << "nn";

myFunction();

cout << "Back from myFunction!nn";

cout << "x from main: " << x << "n";

cout << "y from main: " << y << "n";

return 0;

}

void myFunction()

{

int y = 10;

cout << "x from myFunction: " << x << "n";

cout << "y from myFunction: " << y << "nn";

}

解: 程序运行输出:

x from main: 5

y from main: 7

x from myFunction: 5

y from myFunction: 10

Back from myFunction!

x from main: 5

y from main: 7

5-4 假设有两个无关系的类Engine和Fuel,

使用时,怎样允许Fuel成员访问Engine中的

私有和保护的成员?

解: 源程序:

class fuel;

class engine

{

friend class fuel;

private;

int powerlevel;

public;

engine(){ powerLevel = 0;}

void engine_fn(fuel &f);

};

class fuel

{

friend class engine;

private;

int fuelLevel;

public:

fuel(){ fuelLevel = 0;}

void fuel_fn( engine &e);

};

5-5 什么叫做静态数据成员?它有何特点?

解: 类的静态数据成员是类的数据成员的一

种特例,采用static关键字来声明。对于类的

普通数据成员,每一个类的对象都拥有一个拷

贝,就是说每个对象的同名数据成员可以分别

存储不同的数值,这也是保证对象拥有自身区

别于其它对象的特征的需要,但是静态数据成

员,每个类只要一个拷贝,由所有该类的对象

共同维护和使用,这个共同维护、使用也就实

现了同一类的不同对象之间的数据共享。

5-6 什么叫做静态函数成员?它有何特点?

解: 使用static关键字声明的函数成员是静

态的,静态函数成员属于整个类,同一个类的

所有对象共同维护,为这些对象所共享。静态

函数成员具有以下两个方面的好处,一是由于

静态成员函数只能直接访问同一个类的静态

数据成员,可以保证不会对该类的其余数据成

员造成负面影响;二是同一个类只维护一个静

态函数成员的拷贝,节约了系统的开销,提高

程序的运行效率。

5-7 定义一个Cat类,拥有静态数据成员

HowManyCats,记录Cat的个体数目;静态成

员函数GetHowMany(),存取HowManyCats。

设计程序测试这个类,体会静态数据成员和静

态成员函数的用法。

解: 源程序:

#include

class Cat

{

public:

Cat(int age):itsAge(age){HowManyCats++; }

virtual ~Cat() { HowManyCats--; }

virtual int GetAge() { return itsAge; }

virtual void SetAge(int age) { itsAge = age; }

static int GetHowMany() { return

HowManyCats; }

private:

int itsAge;

static int HowManyCats;

};

int Cat::HowManyCats = 0;

void TelepathicFunction();

int main()

{

const int MaxCats = 5;

Cat *CatHouse[MaxCats]; int i;

for (i = 0; i

{

CatHouse[i] = new Cat(i);

TelepathicFunction();

}

for ( i = 0; i

{

delete CatHouse[i];

TelepathicFunction();

}

return 0;

}

void TelepathicFunction()

{

cout << "There are " << Cat::GetHowMany()

<< " cats alive!n";

}

程序运行输出:

There are 1 cats alive!

There are 2 cats alive!

There are 3 cats alive!

There are 4 cats alive!

There are 5 cats alive!

There are 4 cats alive!

There are 3 cats alive!

There are 2 cats alive!

There are 1 cats alive!

There are 0 cats alive!

5-8 什么叫做友元函数?什么叫做友元类?

解: 友元函数是使用friend关键字声明的函

数,它可以访问相应类的保护成员和私有成

员。友元类是使用friend关键字声明的类,它

的所有成员函数都是相应类的友元函数。

5-9 如果类A是类B的友元,类B是类C

的友元,类D是类A的派生类,那么类B是

类A的友元吗?类C是类A的友元吗?类D

是类B的友元吗?

解: 类B不是类A的友元,友元关系不具有

交换性;

类C不是类A的友元,友元关系不具有传递

性;

类D不是类B的友元,友元关系不能被继承。

5-10 静态成员变量可以为私有的吗?声明一

个私有的静态整型成员变量。

解: 可以,例如:

private:

static int a;

5-11 在一个文件中定义一个全局变量n,主

函数main(),在另一个文件中定义函数fn1(),

在main()中对n赋值,再调用fn1(),在fn1()

中也对n赋值,显示n最后的值。

解: #include

#include "fn1.h"

int n;

void main()

{

n = 20;

fn1();

cout << "n的值为" <

}

// fn1.h文件 extern int n; void fn1()

{

n=30;

}

程序运行输出:

n的值为30

5-12 在函数fn1()中定义一个静态变量n,fn1()

中对n的值加1,在主函数中,调用fn1()十次,

显示n的值。

解: #include

void fn1()

{

static int n = 0;

n++;

cout << "n的值为" << n <

}

void main()

{

for(int i = 0; i < 10; i++)

fn1();

}

程序运行输出:

n的值为1

n的值为2

n的值为3

n的值为4

n的值为5

n的值为6

n的值为7

n的值为8

n的值为9

n的值为10

5-13 定义类X、Y、Z,函数h(X*),满足:

类X有私有成员i,Y的成员函数g(X*)是X

的友元函数,实现对X的成员i加1,类Z是

类X的友元类,其成员函数f(X*)实现对X的

成员i加5,函数h(X*)是X的友元函数,实

现对X的成员i加10。在一个文件中定义和

实现类,在另一个文件中实现main()函数。

解: #include "my_x_y_z.h"

void main() {

X x;

Z z;

z.f(&x);

}

// my_x_y_z.h文件

#ifndef MY_X_Y_Z_H

class X; class Y {

void g(X*);

};

class X { private:

int i;

public:

X(){i=0;}

friend void h(X*);

friend void Y::g(X*);

friend class Z;

};

void h(X* x) { x->i =+10; }

void Y::g(X* x) { x->i ++; }

class Z {

public:

void f(X* x) { x->i += 5; }

};

#endif // MY_X_Y_Z_H

程序运行输出:无

5-14 定义Boat与Car两个类,二者都有weight

属性,定义二者的一个友元函数totalWeight(),

计算二者的重量和。

解: 源程序:

#include

class Boat;

class Car

{

private:

int weight;

public:

Car(int j){weight = j;}

friend int totalWeight(Car &aCar, Boat

&aBoat);

};

class Boat

{

private:

int weight;

public:

Boat(int j){weight = j;}

friend int totalWeight(Car &aCar, Boat

&aBoat);

};

int totalWeight(Car &aCar, Boat &aBoat)

{

return + ;

}

void main()

{

Car c1(4);

Boat b1(5);

cout << totalWeight(c1, b1) << endl;

}

程序运行输出:

9

5-15 如果在类模板的定义中有一个静态数据

成员,则在程序运行中会产生多少个相应的静

态变量?

解: 这个类模板的每一个实例类都会产生一

个相应的静态变量。

第六数组、指针与字符串

6-1 数组A[10][5][15]一共有多少个元素?

解: 10×5×15 = 750 个元素

1-2 在数组A[20]中第一个元素和最后一个元

素是哪一个?

解: 第一个元素是A[0],最后一个元素是

A[19]。

6-3 用一条语句定义一个有五个元素的整型

数组,并依次赋予1~5的初值。

解: 源程序:

int IntegerArray[5] = { 1, 2, 3, 4, 5 };

或:int IntegerArray[] = { 1, 2, 3, 4, 5 };

6-4 已知有一个数组名叫oneArray,用一条语

句求出其元素的个数。

解: 源程序:

nArrayLength = sizeof(oneArray) /

sizeof(oneArray[0]);

6-5 用一条语句定义一个有5×3个元素的二

维整型数组,并依次赋予1~15的初值。

解: 源程序:

int theArray[5][3] = { 1,2,3,4,5,6,7,8,

9,10,11,12,13,14,15 };

或:int theArray[5][3] = { {1,2,3}, {4,5,

6}, {7,8,9}, {10,11,12},{13,14,

15} };

6-6 运算符*和&的作用是什么?

解: *称为指针运算符,是一个一元操作符,

表示指针所指向的对象的值;&称为取地址运

算符,也是一个一元操作符,是用来得到一个

对象的地址。

6-7 什么叫做指针?指针中储存的地址和这

个地址中的值有何区别?

解: 指针是一种数据类型,具有指针类型的

变量称为指针变量。指针变量存放的是另外一

个对象的地址,这个地址中的值就是另一个对

象的内容。

6-8 定义一个整型指针,用new语句为其分配

包含10个整型元素的地址空间。

解: 源程序:

int *pInteger = new int[10];

6-9 在字符串”Hello,world!”中结束符是什

么?

解: 是NULL字符。

6-10 定义一个有五个元素的整型数组,在程

序中提示用户输入元素值,最后再在屏幕上显

示出来。

解: 源程序:

#include int main()

{

int myArray[5];

int i;

for ( i=0; i<5; i++) {

cout << "Value for myArray[" << i << "]: ";

cin >> myArray[i];

}

for (i = 0; i<5; i++)

cout << i << ": " << myArray[i] << "n";

return 0;

}

程序运行输出:

Value for myArray[0]: 2

Value for myArray[1]: 5

Value for myArray[2]: 7

Value for myArray[3]: 8

Value for myArray[4]: 3

0: 2

1: 5

2: 7

3: 8

4: 3

6-11 引用和指针有何区别?何时只能使用指

针而不能使用引用?

解: 引用是一个别名,不能为NULL值,不

能被重新分配;指针是一个存放地址的变量。

当需要对变量重新赋以另外的地址或赋值为

NULL时只能使用指针。

6-12 声明下列指针:float类型变量的指针

pFloat,char类型的指针pString和struct

customer型的指针prec。

解: float *pfloat;

char *pString;

struct customer *prec;

6-13 给定float类型的指针fp,写出显示fp

所指向的值的输出流语句。

解: cout << "Value == " << *fp;

6-14 程序中定义一个double类型变量的指

针。分别显示指针占了多少字节和指针所指的

变量占了多少字节。

解: double *counter;

cout << "nSize of pointer == "sizeof(counter);

cout << 'nSize of addressed value ==

"<

6-15 const int * p1 和 int * const p2的区别是

什么?

解: const int * p1 声明了一个指向整型常量

的指针p1,因此不能通过指针p1来改变它所

指向的整型值;int * const p2声明了一个指针

型常量,用于存放整型变量的地址,这个指针

一旦初始化后,就不能被重新赋值了。

6-16 定义一个整型变量a,一个整型指针p,

一个引用r,通过p把a的值改为10,通过r

把a的值改为5

解: void main()

{

int a;

int *p = &a;

int &r = a;

*p = 10;

r = 5;

}

6-17 下列程序有何问题,请仔细体会使用指

针时应避免出现这个的问题。

#include

int main()

{

int *p;

*pInt = 9;

cout << "The value at p: " << *p;

return 0;

}

解: 指针p没有初始化,也就是没有指向某

个确定的内存单元,它指向内存中的一个随机

地址,给这个随机地址赋值是非常危险的。

6-18 下列程序有何问题,请改正;仔细体会

使用指针时应避免出现的这个问题。

#include

int Fn1();

int main()

{

int a = Fn1();

cout << "the value of a is: " << a;

return 0;

}

int Fn1()

{

int * p = new int (5);

return *p;

}

解: 此程序中给*p分配的内存没有被释放

掉。

改正:

#include

int* Fn1();

int main()

{

int *a = Fn1();

cout << "the value of a is: " << *a;

delete a;

return 0;

}

int* Fn1()

{

int * p = new int (5);

return p;

}

6-19 声明一个参数为整型,返回值为长整型

的函数指针;声明类A的一个成员函数指针,

其参数为整型,返回值长整型。

解: long (* p_fn1)(int);

long ( A::*p_fn2)(int);

6-20 实现一个名为SimpleCircle的简单圆类,

其数据成员int *itsRadius为一个指向其半径

值的指针,设计对数据成员的各种操作,给出

这个类的完整实现并测试这个类。

解: 源程序:

#include

class SimpleCircle

{

public:

SimpleCircle();

SimpleCircle(int);

SimpleCircle(const SimpleCircle &);

~SimpleCircle() {}

void SetRadius(int);

int GetRadius()const;

private:

int *itsRadius;

};

SimpleCircle::SimpleCircle()

{

itsRadius = new int(5);

}

SimpleCircle::SimpleCircle(int radius)

{

itsRadius = new int(radius);

}

SimpleCircle::SimpleCircle(const SimpleCircle

& rhs)

{

int val = ius();

itsRadius = new int(val);

}

int SimpleCircle::GetRadius() const

{

return *itsRadius;

}

int main()

{

SimpleCircle CircleOne, CircleTwo(9);

cout << "CircleOne: " << ius()

<< endl;

cout << "CircleTwo: " <<

ius() << endl;

return 0;

}程序运行输出:

CircleOne: 5

CircleTwo: 9

6-21 编写一个函数,统计一个英文句子中字

母的个数,在主程序中实现输入、输出。

解: 源程序:

#include

#include

int count(char *str)

{

int i,num=0;

for (i=0; str[i]; i++)

{

if ( (str[i]>='a' && str[i]<='z') || (str[i]>='A' &&

str[i]<='Z') )

num++;

}

return num;

}

void main()

{

char text[100];

cout << "输入一个英语句子:" << endl;

gets(text);

cout << "这个句子里有" << count(text) << "个

字母。" << endl;

}

程序运行输出:

输入一个英语句子:

It is very interesting!

这个句子里有19个字母。

6-22 编写函数int index(char *s, char *t),返

回字符串t 在字符串s中出现的最左边的位

置,如果在s中没有与t匹配的子串,就返回

-1。

解: 源程序:

#include

int index( char *s, char *t)

{

int i,j,k;

for(i = 0; s[i] != '0'; i++)

{

for(j = i, k = 0; t[k] != '0' && s[j] == t[k]; j++,

k++)

;

if (t[k] =='0')

return i;

}

return -1;

}

void main()

{

int n;

char str1[20],str2[20];

cout << "输入一个英语单词:";

cin >> str1;

cout << "输入另一个英语单词:";

cin >> str2;

n = index(str1,str2);

if (n > 0)

cout << str2 << "在" << str1 << "中左起第" <<

n+1 << "个位置。"<

else

cout << str2 << "不在" << str1 << "中。" <<

endl;

}

程序运行输出:

输入一个英语单词:abcdefgh

输入另一个英语单词:de

de在abcdefghijk中左起第4个位置。

6-23 编写函数reverse(char *s)的倒序递归程

序,使字符串s倒序。

解: 源程序:

#include

#include

void reverse(char *s, char *t)

{

char c;

if (s < t)

{

c = *s;

*s = *t;

*t = c;

reverse(++s, --t);

}

}

void reverse( char *s)

{

reverse(s, s + strlen(s) - 1);

}

void main()

{

char str1[20];

cout << "输入一个字符串:";

cin >> str1;

cout << "原字符串为:" << str1 << endl;

reverse(str1);

cout << "倒序反转后为:" << str1 << endl;

}

程序运行输出:

输入一个字符串:abcdefghijk

原字符串为:abcdefghijk

倒序反转后为:kjihgfedcba

6-24 设学生人数N=8,提示用户输入N个人

的考试成绩,然后计算出平均成绩,显示出来。

解: 源程序:

#include

#include

#define N 8

float grades[N]; //存放成绩的数组

void main()

{

int i;

float total,average;

//提示输入成绩

for(i = 0; i < N; i++ )

{

cout << "Enter grade #" <<(i +1) << ": ";

cin >> grades[i];

}

total = 0;

for (i = 0; i < N; i++)

total += grades[i];

average = total / N;

cout << "nAverage grade: " << average <<

endl;

}

程序运行输出:

Enter grade #1: 86

Enter grade #2: 98

Enter grade #3: 67

Enter grade #4: 80

Enter grade #5: 78

Enter grade #6: 95

Enter grade #7: 78

Enter grade #8: 56

Average grade: 79.75

6-25 设计一个字符串类MyString,具有构造

函数、析构函数、拷贝构造函数,重载运算符

+、=、+=、[],尽可能地完善它,使之能满足

各种需要。(运算符重载功能为选做,参见第

8章)

解: #include

#include

class MyString

{

public:

MyString();

MyString(const char *const);

MyString(const MyString &);

~MyString();

char & operator[](unsigned short offset);

char operator[](unsigned short offset) const;

MyString operator+(const MyString&);

void operator+=(const MyString&);

MyString & operator= (const MyString &);

unsigned short GetLen()const { return itsLen; }

const char * GetMyString() const { return

itsMyString; }

private:

MyString (unsigned short); // private constructor

char * itsMyString;

unsigned short itsLen;

};

MyString::MyString()

{

itsMyString = new char[1];

itsMyString[0] = '0';

itsLen=0;

}

MyString::MyString(unsigned short len)

{

itsMyString = new char[len+1];

for (unsigned short i = 0; i<=len; i++)

itsMyString[i] = '0';

itsLen=len;

}

MyString::MyString(const char * const

cMyString)

{

itsLen = strlen(cMyString);

itsMyString = new char[itsLen+1];

for (unsigned short i = 0; i

itsMyString[i] = cMyString[i];

itsMyString[itsLen]='0';

}

MyString::MyString (const MyString & rhs)

{

itsLen=();

itsMyString = new char[itsLen+1];

for (unsigned short i = 0; i

itsMyString[i] = rhs[i];

itsMyString[itsLen] = '0';

}

MyString::~MyString ()

{

delete [] itsMyString;

itsLen = 0;

}

MyString& MyString::operator=(const

MyString & rhs)

{

if (this == &rhs)

return *this;

delete [] itsMyString;

itsLen=();

itsMyString = new char[itsLen+1];

for (unsigned short i = 0; i

itsMyString[i] = rhs[i];

itsMyString[itsLen] = '0';

return *this;

}

char & MyString::operator[](unsigned short

offset)

{

if (offset > itsLen)

return itsMyString[itsLen-1];

else

return itsMyString[offset];

}

char MyString::operator[](unsigned short offset)

const

{

if (offset > itsLen)

return itsMyString[itsLen-1];

else

return itsMyString[offset];

}

MyString MyString::operator+(const

MyString& rhs)

{

unsigned short totalLen = itsLen + ();

MyString temp(totalLen);

for (unsigned short i = 0; i

temp[i] = itsMyString[i];

for (unsigned short j = 0; j<(); j++,

i++)

temp[i] = rhs[j];

temp[totalLen]='0';

return temp;

}

void MyString::operator+=(const MyString&

rhs)

{

unsigned short rhsLen = ();

unsigned short totalLen = itsLen + rhsLen;

MyString temp(totalLen);

for (unsigned short i = 0; i

temp[i] = itsMyString[i];

for (unsigned short j = 0; j<(); j++,

i++)

temp[i] = rhs[i-itsLen];

temp[totalLen]='0';

*this = temp;

}

int main()

{

MyString s1("initial test");

cout << "S1:t" << tring() << endl;

char * temp = "Hello World";

s1 = temp;

cout << "S1:t" << tring() << endl;

char tempTwo[20];

strcpy(tempTwo,"; nice to be here!");

s1 += tempTwo;

cout << "tempTwo:t" << tempTwo << endl;

cout << "S1:t" << tring() << endl;

cout << "S1[4]:t" << s1[4] << endl;

s1[4]='x';

cout << "S1:t" << tring() << endl;

cout << "S1[999]:t" << s1[999] << endl;

MyString s2(" Another myString");

MyString s3;

s3 = s1+s2;

cout << "S3:t" << tring() << endl;

MyString s4;

s4 = "Why does this work?";

cout << "S4:t" << tring() << endl;

return 0;

}

程序运行输出:

S1: initial test

S1: Hello World

tempTwo: ; nice to be here!

S1: Hello World; nice to be here!

S1[4]: o

S1: Hellx World; nice to be here!

S1[999]: !

S3: Hellx World; nice to be here! Another

myString

S4: Why does this work?

6-26 编写一个3×3矩阵转置的函数,在

main()函数中输入数据。

解: #include

void move (int matrix[3][3])

{

int i, j, k;

for(i=0; i<3; i++)

for (j=0; j

{

k = matrix[i][j];

matrix[i][j] = matrix[j][i];

matrix[j][i] = k;

}

}

void main()

{

int i, j;

int data[3][3];

cout << "输入矩阵的元素" << endl;

for(i=0; i<3; i++)

for (j=0; j<3; j++)

{

cout << "第" << i+1 << "行第" << j+1

<<"个元素为:";

cin >> data[i][j];

}

cout << "输入的矩阵的为:" << endl;

for(i=0; i<3; i++)

{

for (j=0; j<3; j++)

cout << data[i][j] << " ";

cout << endl;

}

move(data);

cout << "转置后的矩阵的为:" << endl;

for(i=0; i<3; i++)

{

for (j=0; j<3; j++)

cout << data[i][j] << " ";

cout << endl;

}

} 程序运行输出:

输入矩阵的元素

第1行第1个元素为:1

第1行第2个元素为:2

第1行第3个元素为:3

第2行第1个元素为:4

第2行第2个元素为:5

第2行第3个元素为:6

第3行第1个元素为:7

第3行第2个元素为:8

第3行第3个元素为:9

输入的矩阵的为:

1 2 3 4 5 6 7 8 9 转置后的矩阵的为:

1 4 7 2 5 8 3 6 9 6-27 编写一个矩阵转置的函

数,矩阵的维数在程序中由用户输入。

解: #include

void move (int *matrix ,int n)

{

int i, j, k;

for(i=0; i

for (j=0; j

{

k = *(matrix + i*n + j);

*(matrix + i*n + j) = *(matrix + j*n + i);

*(matrix + j*n + i) = k;

}

}

void main()

{

int n, i, j;

int *p;

cout << "请输入矩阵的维数:";

cin >> n;

p = new int[n*n];

cout << "输入矩阵的元素" << endl;

for(i=0; i

for (j=0; j

{

cout << "第" << i+1 << "行第" << j+1

<<"个元素为:";

cin >> p[i*n + j];

}

cout << "输入的矩阵的为:" << endl;

for(i=0; i

{

for (j=0; j

cout << p[i*n + j] << " ";

cout << endl;

}

move(p, n);

cout << "转置后的矩阵的为:" << endl;

for(i=0; i

{

for (j=0; j

cout << p[i*n + j] << " ";

cout << endl;

}

} 程序运行输出:

请输入矩阵的维数:3

输入矩阵的元素

第1行第1个元素为:1

第1行第2个元素为:2

第1行第3个元素为:3

第2行第1个元素为:4

第2行第2个元素为:5

第2行第3个元素为:6

第3行第1个元素为:7

第3行第2个元素为:8

第3行第3个元素为:9

输入的矩阵的为:

1 2 3 4 5 6 7 8 9 转置后的矩阵的为:

1 4 7 2 5 8 3 6 9

6-28 定义一个Employee类,其中包括表示姓

名、街道地址、城市和邮编等属性,包括

chage_name()和display()等函数;display()使用

cout语句显示姓名、街道地址、城市和邮编等

属性,函数change_name()改变对象的姓名属

性,实现并测试这个类。

解: 源程序:

#include

#include

class Employee

{

private:

char name[30];

char street[30];

char city[18];

char zip[6];

public:

Employee(char *n, char *str, char *ct, char *z);

void change_name(char *n);

void display();

};

Employee::Employee (char *n,char *str,char *ct,

char *z)

{

strcpy(name, n);

strcpy(street, str);

strcpy(city, ct);

strcpy(zip, z);

}

void Employee::change_name (char *n)

{

strcpy(name, n);

}

void Employee::display ()

{

cout << name << " " << street << " ";

cout << city << " "<< zip;

}

void main(void)

{

Employee e1("张三","平安大街3号", "北京",

"100000");

y();

cout << endl;

_name("李四");

y();

cout << endl;

}

程序运行输出:

张三 平安大街3号 北京 100000

李四 平安大街3号 北京 100000

第 七 章 继承与派生

7-1 比较类的三种继承方式public公有继承、

protected保护继承、private私有继承之间的差

别。

解: 不同的继承方式,导致不同访问属性的

基类成员在派生类中的访问属性也有所不同:

公有继承,使得基类public(公有)和

protected(保护)成员的访问属性在派生类中不

变,而基类private(私有)成员不可访问。

私有继承,使得基类public(公有)和

protected(保护)成员都以private(私有)成员身

份出现在派生类中,而基类private(私有)成员

不可访问。

保护继承中,基类public(公有)和protected(保

护)成员都以protected(保护)成员身份出现在

派生类中,而基类private(私有)成员不可访

问。 7-2 派生类构造函数执行的次序是怎样

的?

解: 派生类构造函数执行的一般次序为:调

用基类构造函数;调用成员对象的构造函数;

派生类的构造函数体中的内容。

7-3 如果在派生类B已经重载了基类A的一

个成员函数fn1(),没有重载成员函数fn2(),

如何调用基类的成员函数fn1()、fn2()?

解: 调用方法为: A::fn1(); fn2();

7-4 什么叫做虚基类?有何作用?

解: 当某类的部分或全部直接基类是从另一

个基类派生而来,这些直接基类中,从上一级

基类继承来的成员就拥有相同的名称,派生类

的对象的这些同名成员在内存中同时拥有多

个拷贝,我们可以使用作用域分辨符来唯一标

识并分别访问它们。我们也可以将直接基类的

共同基类设置为虚基类,这时从不同的路径继

承过来的该类成员在内存中只拥有一个拷贝,

这样就解决了同名成员的唯一标识问题。

虚基类的声明是在派生类的定义过程,其语法

格式为:

class 派生类名:virtual 继承方式 基类名

上述语句声明基类为派生类的虚基类,在多继

承情况下,虚基类关键字的作用范围和继承方

式关键字相同,只对紧跟其后的基类起作用。

声明了虚基类之后,虚基类的成员在进一步派

生过程中,和派生类一起维护一个内存数据拷

贝。

7-5 定义一个Shape基类,在此基础上派生出

Rectangle和Circle,二者都有GetArea()函数

计算对象的面积。使用Rectangle类创建一个

派生类Square。

解: 源程序:

#include

class Shape

{

public:

Shape(){}

~Shape(){}

virtual float GetArea() { return -1; } };

class Circle : public Shape

{

public:

Circle(float radius):itsRadius(radius){}

~Circle(){}

float GetArea() { return 3.14 * itsRadius *

itsRadius; }

private:

float itsRadius;

};

class Rectangle : public Shape

{

public:

Rectangle(float len, float width):

itsLength(len), itsWidth(width){};

~Rectangle(){};

virtual float GetArea() { return itsLength *

itsWidth; }

virtual float GetLength() { return itsLength; }

virtual float GetWidth() { return itsWidth; }

private:

float itsWidth;

float itsLength;

};

class Square : public Rectangle

{

public:

Square(float len);

~Square(){}

};

Square::Square(float len):

Rectangle(len,len)

{

}

void main()

{

Shape * sp;

sp = new Circle(5);

cout << "The area of the Circle is " <<

sp->GetArea () << endl;

delete sp;

sp = new Rectangle(4,6);

cout << "The area of the Rectangle is " <<

sp->GetArea() << endl;

delete sp;

sp = new Square(5);

cout << "The area of the Square is " <<

sp->GetArea() << endl;

delete sp;

}

程序运行输出:

The area of the Circle is 78.5

The area of the Rectangle is 24

The area of the Square is 25

7-6 定义一个哺乳动物Mammal类,再由此派

生出狗Dog类,定义一个Dog类的对象,观

察基类与派生类的构造函数与析构函数的调

用顺序。

解: 源程序:

#include

enum myColor{ BLACK, WHITE };

class Mammal

{

public:

// constructors

Mammal();

~Mammal();

//accessors

int GetAge() const { return itsAge; }

void SetAge(int age) { itsAge = age; }

int GetWeight() const { return itsWeight; }

void SetWeight(int weight) { itsWeight =

weight; }

//Other methods

void Speak() const { cout << "Mammal

sound!n"; }

protected:

int itsAge;

int itsWeight;

};

class Dog : public Mammal

{

public:

Dog();

~Dog();

myColor GetColor() const { return itsColor; }

void SetColor (myColor color) { itsColor =

color; }

void WagTail() { cout << ""; }

private:

myColor itsColor;

};

Mammal::Mammal():

itsAge(1),

itsWeight(5)

{

cout << "";

}

Mammal::~Mammal()

{

cout << "";

}

Dog::Dog(): itsColor (WHITE)

{

cout << "";

}

Dog::~Dog()

{

cout << "";

}

int main()

{

Dog Jack;

();

l();

cout << " Jack is " << () << " years

oldn";

return 0;

}

程序运行输出:

Mammal sound!

Fido is 1 years old

7-7 定义一个基类,构造其派生类,在构造

函数中输出提示信息,观察构造函数的执行情

况。

解: #include

class BaseClass

{

public:

BaseClass();

};

BaseClass::BaseClass()

{

cout << "构造基类对象!" << endl;

}

class DerivedClass : public BaseClass

{

public:

DerivedClass(); };

DerivedClass::DerivedClass()

{

cout << "构造派生类对象!" << endl;

}

void main()

{

DerivedClass d;

}

程序运行输出:

构造基类对象!

构造派生类对象!

7-8 定义一个Document类,有name成员变

量,从Document派生出Book类,增加

PageCount变量。

解: #include

#include

class Document

{

public:

Document(){};

Document( char *name );

char *Name; // Document name.

void PrintNameOf(); // Print name.

};

Document::Document( char *name )

{

Name = new char[ strlen( name ) + 1 ];

strcpy( Name, name );

};

void Document::PrintNameOf()

{

cout << Name << endl;

}

class Book : public Document

{

public:

Book( char *name, long pagecount );

void PrintNameOf();

private:

long PageCount;

};

Book::Book( char *name, long

pagecount ):Document(name)

{

PageCount = pagecount;

}

void Book::PrintNameOf()

{

cout << "Name of book: ";

Document::PrintNameOf();

}

void main()

{

Document a("Document1");

Book b("Book1",100);

ameOf();

}

程序运行输出:

Name of book: Book1

7-9 定义基类Base,有两个共有成员函数

fn1()、fn2(),私有派生出Derived类,如果想

在Derived类的对象中使用基类函数fn1(),应

怎么办?

解: class Base {

public:

int fn1() const { return 1; }

int fn2() const { return 2; }

};

class Derived : private Base { public:

int fn1() { return Base::fn1();}; int fn2() { return

Base::fn2();};

};

void main()

{

Derived a;

1();

}

7-10 定义object类,有weight属性及相应的

操作函数,由此派生出box类,增加Height

和width属性及相应的操作函数,声明一个

box对象,观察构造函数与析构函数的调用顺

序。

解: #include

class object

{

private:

int Weight;

public:

object()

{ cout << "构造object对象" << endl;

Weight = 0;

}

int GetWeight(){ return Weight;} void

SetWeight(int n){ Weight = n;}

~object() { cout << "析构object对象" <<

endl;}

};

class box : public object

{

private:

int Height,Width;

public:

box()

{ cout << "构造box对象" << endl;

Height = Width = 0;

}

int GetHeight(){ return Height;} void

SetHeight(int n){ Height = n;}

int GetWidth(){ return Width;} void

SetWidth(int n){ Width = n;}

~box() { cout << "析构box对象" << endl;}

};

void main()

{

box a;

}

程序运行输出:

构造object对象

构造box对象

析构box对象

析构object对象

7-11 定义一个基类BaseClass,从它派生出

类DerivedClass,BaseClass有成员函数fn1()、

fn2(),DerivedClass也有成员函数fn1()、fn2(),

在主程序中定义一个DerivedClass的对象,分

别用DerivedClass的对象以及BaseClass和

DerivedClass的指针来调用fn1()、fn2(),观察

运行结果。

解: #include

class BaseClass

{

public:

void fn1();

void fn2();

};

void BaseClass::fn1()

{

cout << "调用基类的函数fn1()" << endl;

}

void BaseClass::fn2()

{

cout << "调用基类的函数fn2()" << endl;

}

class DerivedClass : public BaseClass

{

public:

void fn1();

void fn2();

};

void DerivedClass::fn1()

{

cout << "调用派生类的函数fn1()" << endl;

}

void DerivedClass::fn2()

{

cout << "调用派生类的函数fn2()" << endl;

}

void main()

{

DerivedClass aDerivedClass;

DerivedClass *pDerivedClass =

&aDerivedClass;

BaseClass *pBaseClass = &aDerivedClass;

1();

2();

pBaseClass->fn1();

pBaseClass->fn2();

pDerivedClass->fn1();

pDerivedClass->fn2();

}

程序运行输出:

调用派生类的函数fn1()

调用派生类的函数fn2()

调用基类的函数fn1()

调用基类的函数fn2()

调用派生类的函数fn1()

调用派生类的函数fn2()

7-12 为例9-1的吹泡泡程序加一版权(About)

对话框。

然后修改例9-1的程序,加入以下内容:

程 序:

1.在程序首部加上文件包含命令

#include “resource.h”

2.在框架窗口类之前加入从CDialog类派生

的对话框类:

// 对话框类

class CAboutDlg: public CDialog

{

public:

CAboutDlg();

enum {IDD = IDD_DIALOG1};

};

inline

CAboutDlg::CAboutDlg():CDialog(CAboutDlg:

:IDD){}

3.在框架窗口类中添加响应鼠标右键消息的

代码,包括消息响应函数说明、消息响应宏和

消息响应函数定义。鼠标右键消息响应函数

为:

void CMyWnd::OnRButtonDown(UINT nFlags,

CPoint point)

{

CAboutDlg dlg;

l();

}

7-13 签名留念簿程序。该程序模仿签名簿,

用户使用鼠标左键点击窗口客户区后会弹出

一个对话框,输入姓名后可在鼠标点击位置显

示出该签名。签名的颜色、字体大小和方向随

机确定。

说 明:项目建立及添加对话框模板资源的方

法同例14-1。修改对话框模板的ID为

IDD_NAMEDLG,Caption为“签名对话框”,

并添加一个静态文本控件(Caption改为“签

名”)和一个编辑控件(ID改为

IDC_EDITNAME)。

程 序:

// Example 14-2:签名留念簿程序

#include

#include "resource.h"

// 对话框类

class CNameDlg: public CDialog

{

public:

CPoint m_pointTopLeft;

CString m_strNameEdit;

public:

CNameDlg();

enum {IDD = IDD_NAMEDLG};

protected:

virtual void DoDataExchange(CDataExchange*

pDX);

virtual BOOL OnInitDialog(); };

// 对话框类的构造函数

CNameDlg::CNameDlg():CDialog(CNameDlg::

IDD)

{

m_strNameEdit = _T("");

}

// 数据交换和数据检验

void

CNameDlg::DoDataExchange(CDataExchange*

pDX)

{

CDialog::DoDataExchange(pDX);

DDX_Text(pDX, IDC_EDITNAME,

m_strNameEdit);

DDV_MaxChars(pDX, m_strNameEdit, 20);

}

// 初始化对话框

BOOL CNameDlg::OnInitDialog() {

CDialog::OnInitDialog();

CRect rect;

GetWindowRect(&rect);

rect = CRect(m_pointTopLeft, ());

MoveWindow(rect);

return TRUE;

}

// 签名类

class CSignal: public CObject

{

CString m_sName; // 姓名

CPoint m_pointSignal; // 签名位置

int m_nHeight; // 字体高

int m_nColor; // 签名颜色

int m_nEscapement; // 签名倾角

public:

CSignal(){}

void SetValue(CString name,CPoint point,int

height,int color,

int escapement);

void ShowSignal(CDC *pDC);

};

// 签名类成员函数

void CSignal::SetValue(CString name,CPoint

point,int height,int color, int escapement)

{

m_sName = name;

m_pointSignal = point;

m_nHeight = height;

m_nColor = color;

m_nEscapement = escapement;

}

// 显示签名

void CSignal::ShowSignal(CDC *pDC)

{

CFont *pOldFont, font;

Font(m_nHeight, 0,

m_nEscapement,0, 400, FALSE,FALSE,

0, OEM_CHARSET,

OUT_DEFAULT_PRECIS,

CLIP_DEFAULT_PRECIS,DEFAULT_QUALI

TY, DEFAULT_PITCH, "楷体");

pOldFont = pDC->SelectObject(&font);

switch(m_nColor)

{

case 0:

pDC->SetTextColor(RGB(0, 0, 0));

break;

case 1:

pDC->SetTextColor(RGB(255, 0, 0));

break;

case 2:

pDC->SetTextColor(RGB(0, 255, 0));

break;

case 3:

pDC->SetTextColor(RGB(0, 0, 255));

break;

}

pDC->TextOut(m_pointSignal.x,

m_pointSignal.y, m_sName);

pDC->SelectObject(pOldFont);

}

// 框架窗口类

#define MAX_NAME 250

class CMyWnd: public CFrameWnd

{

CSignal m_signalList[MAX_NAME];

int m_nCount;

public:

CMyWnd(): m_nCount(0){}

protected:

afx_msg void OnLButtonDown(UINT nFlags,

CPoint point);

afx_msg void OnPaint();

DECLARE_MESSAGE_MAP()

};

// 消息映射

BEGIN_MESSAGE_MAP(CMyWnd,

CFrameWnd)

ON_WM_LBUTTONDOWN()

ON_WM_PAINT()

END_MESSAGE_MAP()

// 框架窗口类的成员函数

// 鼠标右键消息响应函数

void CMyWnd::OnLButtonDown(UINT nFlags,

CPoint point)

{

if(m_nCount < MAX_NAME)

{

CNameDlg dlg;

dlg.m_pointTopLeft = point;

if(l() == IDOK)

{

int height = rand()%60+12;

int color = rand()%4;

int escapement = (rand()%1200)-600;

CString name = dlg.m_strNameEdit;

m_signalList[m_nCount].SetValue(name,point,h

eight,

color,escapement);

m_nCount++;

Invalidate();

}

}

}

// 绘制框架窗口客户区函数

void CMyWnd::OnPaint()

{

CPaintDC dc(this);

for(int i=0; i

m_signalList[i].ShowSignal(&dc);

}

// 应用程序类

class CMyApp: public CWinApp

{

public:

BOOL InitInstance();

};

// 应用程序类的成员函数

BOOL CMyApp::InitInstance()

{

CMyWnd *pFrame = new CMyWnd;

pFrame->Create(0,_T("签字留念簿程序"));

pFrame->ShowWindow(m_nCmdShow);

this->m_pMainWnd = pFrame;

return TRUE;

}

// 全局应用程序对象

CMyApp ThisApp;

7-14 将例14-2的签名留念簿中的对话框改

为无模式对话框。用户可用鼠标右键调出签名

对话框,并在不退出该对话框的情况下用鼠标

左键将输入的签名显示在窗口客户区。

说 明:在向项目中添加对话框模板资源时,

要在其属性对话框的More Styles页中选择

Visible项。其他同例14-2。

程 序:

该程序中的签名类CSignal和应用程序类与上

例相同,因此下面仅列出了框架窗口类和对话

框类。由于这两个类的成员函数中存在相互引

用的情况,所以我们将框架窗口类的声明放在

前面,接下来是对话框类的定义,并在框架窗

口类之前加入了一条对对话框类的声明。最后

是这两个类的成员函数定义。

// 框架窗口类

#define MAX_NAME 250

class CNameDlg;

class CMyWnd: public CFrameWnd

{

CSignal m_signalList[MAX_NAME];

int m_nCount;

CNameDlg *m_pNameDlg;

public:

CMyWnd();

~CMyWnd();

protected:

afx_msg void OnLButtonDown(UINT nFlags,

CPoint point);

afx_msg void OnRButtonDown(UINT nFlags,

CPoint point);

afx_msg void OnPaint();

DECLARE_MESSAGE_MAP()

};

// 对话框类

class CNameDlg: public CDialog

{

public:

BOOL m_bActive;

CString m_strNameEdit;

enum {IDD = IDD_NAMEDLG};

CNameDlg();

BOOL Create();

protected:

virtual void DoDataExchange(CDataExchange*

pDX);

virtual BOOL OnInitDialog();

virtual void OnOK();

virtual void OnCancel();

};

// 框架窗口类的消息映射

BEGIN_MESSAGE_MAP(CMyWnd,

CFrameWnd)

ON_WM_LBUTTONDOWN()

ON_WM_RBUTTONDOWN()

ON_WM_PAINT()

END_MESSAGE_MAP()

// 框架窗口类的成员函数

// 框架窗口类的构造函数

CMyWnd::CMyWnd()

{

m_nCount = 0;

m_pNameDlg = new CNameDlg;

}

// 框架窗口类的析构函数

CMyWnd::~CMyWnd()

{

delete m_pNameDlg;

}

// 鼠标右键消息响应函数

void CMyWnd::OnRButtonDown(UINT nFlags,

CPoint point)

{

if(m_pNameDlg->m_bActive)

m_pNameDlg->SetActiveWindow(); // 激活对

话框

else

m_pNameDlg->Create(); // 显示对话框

}

// 鼠标左键消息响应函数

void CMyWnd::OnLButtonDown(UINT nFlags,

CPoint point)

{

if(m_nCount < MAX_NAME)

{

int height = rand()%60+12;

int color = rand()%4;

int escapement = (rand()%1200)-600;

CString name =

m_pNameDlg->m_strNameEdit;

m_signalList[m_nCount].SetValue(name, point,

height, color,

escapement);

m_nCount++;

Invalidate();

}

}

// 绘制框架窗口客户区函数

void CMyWnd::OnPaint()

{

CPaintDC dc(this);

for(int i=0; i

m_signalList[i].ShowSignal(&dc);

}

// 对话框类的成员函数

// 对话框类的构造函数

CNameDlg::CNameDlg():CDialog(CNameDlg::

IDD)

{

m_bActive = FALSE;

m_strNameEdit = _T("");

}

// 数据交换

void

CNameDlg::DoDataExchange(CDataExchange*

pDX)

{

CDialog::DoDataExchange(pDX);

DDX_Text(pDX, IDC_EDIT1,

m_strNameEdit);

}

// 初始化对话框

BOOL CNameDlg::OnInitDialog() {

CDialog::OnInitDialog();

CRect rect;

GetWindowRect(&rect);

MoveWindow(0, 0, (), ());

return TRUE;

}

// 显示无模态对话框

BOOL CNameDlg::Create()

{

m_bActive = TRUE;

return CDialog::Create(CNameDlg::IDD);

}

// 退出对话框

void CNameDlg::OnCancel() {

m_bActive = FALSE;

DestroyWindow(); }

// 更新数据

void CNameDlg::OnOK()

{

UpdateData(TRUE);

}

7-15 为例14-2的签名程序加上字体选择对

话框。

说 明:本程序使用字体选择公用对话框(通

过鼠标右键调出)选择签名的字体、字号和颜

色等参数,在签名对话框中要输入姓名和签名

与X轴的倾斜角。建立项目的方法与例14-2

相似,只是要在签名对话框模板中再添加一个

编辑控件用于输入签名的倾斜角,其标识符为

IDD_EDIT2。

程 序:

// Example 14-4:签名留念簿程序

#include

#include

#include

#include "resource.h"

// 对话框类

class CNameDlg: public CDialog

{

public:

CPoint m_pointTopLeft; // 对话框位置

CString m_strNameEdit; // 签名

LONG m_lEscapement; // 签名倾角

public:

CNameDlg();

enum {IDD = IDD_NAMEDLG};

protected:

virtual void DoDataExchange(CDataExchange*

pDX);

virtual BOOL OnInitDialog(); };

// 对话框类的构造函数

CNameDlg::CNameDlg():CDialog(CNameDlg::

IDD), m_pointTopLeft(0, 0)

{

m_strNameEdit = _T("");

m_lEscapement = 0;

}

// 数据交换和数据检验

void

CNameDlg::DoDataExchange(CDataExchange*

pDX)

{

CDialog::DoDataExchange(pDX);

DDX_Text(pDX, IDC_EDIT1,

m_strNameEdit);

DDX_Text(pDX, IDC_EDIT2,

m_lEscapement);

DDV_MaxChars(pDX, m_strNameEdit, 20);

DDV_MinMaxLong(pDX, m_lEscapement,

-600, 600);

}

// 初始化对话框

BOOL CNameDlg::OnInitDialog() {

CDialog::OnInitDialog();

CRect rect;

GetWindowRect(&rect);

rect = CRect(m_pointTopLeft, ());

MoveWindow(rect);

return TRUE;

}

// 签名类

class CSignal: public CObject

{

CString m_strSignal; // 姓名

COLORREF m_colorSignal; // 签名颜色

CPoint m_pointSignal; // 签名位置

LOGFONT m_fontSignal; // 签名字体

public:

CSignal(){}

void SetValue(CString signal, CPoint point,

COLORREF color,

LONG escapement, LOGFONT *pfont);

void ShowSignal(CDC *pDC);

};

// 签名类成员函数

void CSignal::SetValue(CString signal, CPoint

point, COLORREF color,

int escapement, LOGFONT *pfont)

{

m_strSignal = signal;

m_pointSignal = point;

m_colorSignal = color;

memcpy(&m_fontSignal, pfont,

sizeof(LOGFONT));

m_pement = escapement;

}

// 显示签名

void CSignal::ShowSignal(CDC *pDC)

{

CFont font, *pOldFont;

FontIndirect(&m_fontSignal);

pOldFont = pDC->SelectObject(&font);

pDC->SetTextColor(m_colorSignal);

pDC->TextOut(m_pointSignal.x,

m_pointSignal.y, m_strSignal);

pDC->SelectObject(pOldFont);

}

// 框架窗口类

#define MAX_NAME 250

class CMyWnd: public CFrameWnd

{

CSignal m_signalList[MAX_NAME]; // 签名

数组

int m_nCount; // 签名数量

LOGFONT m_fontSignal; // 签名字体

COLORREF m_colorSignal; // 签名颜色

public:

CMyWnd();

protected:

afx_msg void OnLButtonDown(UINT nFlags,

CPoint point);

afx_msg void OnRButtonDown(UINT nFlags,

CPoint point);

afx_msg void OnPaint();

DECLARE_MESSAGE_MAP()

};

// 消息映射

BEGIN_MESSAGE_MAP(CMyWnd,

CFrameWnd)

ON_WM_LBUTTONDOWN()

ON_WM_RBUTTONDOWN()

ON_WM_PAINT()

END_MESSAGE_MAP()

// 框架窗口类的成员函数

CMyWnd::CMyWnd()

{

m_nCount = 0;

m_colorSignal = RGB(0, 0, 0);

m_ht = 40;

m_h = 0;

m_pement = 0;

m_ntation = 0;

m_ht = 400;

m_ic = FALSE;

m_rline = FALSE;

m_keOut = 0;

m_Set = OEM_CHARSET;

m_recision =

OUT_DEFAULT_PRECIS;

m_Precision =

CLIP_DEFAULT_PRECIS;

m_ity =

DEFAULT_QUALITY;

m_hAndFamily =

DEFAULT_PITCH;

strcpy(m_Name, "Arial"); }

// 鼠标右键消息响应函数

void CMyWnd::OnLButtonDown(UINT nFlags,

CPoint point)

{

if(m_nCount < MAX_NAME)

{

CNameDlg dlg;

dlg.m_pointTopLeft = point;

if(l() == IDOK)

{

LONG escapement = dlg.m_lEscapement;

CString name = dlg.m_strNameEdit;

m_signalList[m_nCount].SetValue(name, point,

m_colorSignal,

escapement, &m_fontSignal);

m_nCount++;

Invalidate();

}

}

}

// 鼠标右键消息响应函数

void CMyWnd::OnRButtonDown(UINT nFlags,

CPoint point)

{

CFontDialog dlg(&m_fontSignal);

if(l() == IDOK)

{

rentFont(&m_fontSignal);

m_colorSignal = or();

}

}

// 绘制框架窗口客户区函数

void CMyWnd::OnPaint()

{

CPaintDC dc(this);

for(int i=0; i

m_signalList[i].ShowSignal(&dc);

}

// 应用程序类

class CMyApp: public CWinApp

{

public:

BOOL InitInstance();

};

// 应用程序类的成员函数

BOOL CMyApp::InitInstance()

{

CMyWnd *pFrame = new CMyWnd;

pFrame->Create(0,_T("签字留念簿程序"));

pFrame->ShowWindow(SW_SHOWMAXIMIZ

ED);

this->m_pMainWnd = pFrame;

return TRUE;

}

// 全局应用程序对象

CMyApp ThisApp;

7-16 为例9-3的吹泡泡程序添加颜色选择对

话框,使其可以绘出五颜六色的泡泡。

程 序:在例9-3的程序基础上作如下修改:

1.在程序首部添加文件包含命令:

#include

2.在框架窗口类声明中添加一个COLORREF

类型的数组,存放各泡泡的颜色:

COLORREF m_colorBubble

[MAX_BUBBLE];

3.修改鼠标左键消息映射函数,添加使用颜

色选择公用对话框的代码:

void CMyWnd::OnLButtonDown ( UINT

nFlags, CPoint point )

{

if(m_nBubbleCount < MAX_BUBBLE)

{

m_colorBubble[m_nBubbleCount] = RGB(200,

200, 200);

CColorDialog

dlg(m_colorBubble[m_nBubbleCount]);

if(l() == IDOK)

m_colorBubble[m_nBubbleCount] =

or();

int r = rand()%50+10;

CRect rect(point.x-r, point.y-r, point.x+r,

point.y+r);

m_rectBubble[m_nBubbleCount] = rect;

m_nBubbleCount++;

InvalidateRect(rect, FALSE);

}

}

4.修改OnPaint()成员函数,添加根据泡

泡颜色使用画刷的代码:

void CMyWnd::OnPaint()

{

CPaintDC dc(this);

CBrush brushNew, *pbrushOld;

for(int i=0; i

{

SolidBrush(m_colorBubble[i]);

pbrushOld = Object(&brushNew);

e(m_rectBubble[i]);

Object(pbrushOld);

Object();

}

}

7-17 序列化。如果例12-1的吹泡泡程序使用

一般的数组存放泡泡数据(参看例9-1的程

序):

CRect m_rectBubble[MAX_BUBBLE];

int m_nBubbleCount;

为其文档类重新设计Serialize()函数。

说 明:按例12-1的方法建立项目和输入源代

码,但将文档类中的泡泡数据改为以上两行的

形式。修改文档类的Serialize()函数,代码

如下。

程 序:

// 序列化函数

void CMyDoc::Serialze(CArchive& ar)

{

if(ing())

{

ar << m_nBubbleCount;

for(int i=0; i

ar << m_rectBubble[i];

}

else

{

ar >> m_nBubbleCount;

for(int i=0; i

ar >> m_rectBubble[i];

}

}

7-18 修改例12-1的程序并观察其打印结果。

程 序:

在例12-1程序的视图类CMyView类的成员函

数OnDraw()中,添加代码沿窗口客户区轮

廓画一矩形:

void CMyView::OnDraw(CDC* pDC)

{

CRect rect;

GetClientRect(&rect);

pDC->Rectangle(rect);

CMyDoc* pDoc = GetDocument(); // 取文档

指针

ASSERT_VALID(pDoc);

pDC->SelectStockObject(LTGRAY_BRUSH);

// 在视图上显示文档数据

for(int i=0; iGetListSize(); i++)

pDC->Ellipse(pDoc->GetBubble(i));

}

7-19 改进吹泡泡程序,使之打印输出与屏幕

显示的比例相近。

程 序:

在例12-1基础上修改。首先在CMyView类中

重载虚函数OnPrepareDC()。在CMyView

类的声明中增加一行:

virtual void OnPrepareDC(CDC *pDC,

CPrintInfo *pInfo=NULL);

然后添加该函数的定义,设置映射模式为

MM_LOMETRIC:

// 设置映射模式

void CMyView::OnPrepareDC(CDC *pDC,

CPrintInfo *pInfo)

{

pDC->SetMapMode(MM_LOMETRIC);

CView::OnPrepareDC(pDC, pInfo);

}

然后修改消息映射函数OnLButtonDown(),

将物理坐标转换为逻辑坐标:

// 响应点击鼠标左键消息

void CMyView::OnLButtonDown(UINT nFlags,

CPoint point)

{

CMyDoc* pDoc = GetDocument(); // 取文档

指针

ASSERT_VALID(pDoc);

CClientDC dc(this); // 设置设备环境

OnPrepareDC(&dc);

int r = rand()%50+5; // 生成泡泡

CRect rect(point.x-r, point.y-r, point.x+r,

point.y+r);

InvalidateRect(rect, FALSE); // 更新视图

(rect); // 转换物理坐标为逻辑坐标

pDoc->AddBubble(rect); // 修改文档数据

pDoc->SetModifiedFlag(); // 设置修改标志

}

7-20 声明一个Person类,并使之支持序列化。

程 序:

class CPerson: public CObject

{

DECLARE_SERIAL( CPerson)

LONG m_IDnumber; // 身份证号码

CString m_strName; // 姓名

CString m_strNation; // 民族

int m_nSex; // 性别

int m_nAge; // 年龄

BOOL m_bMarried; // 婚否

public:

CEmployee(){};

CPerson& operator = (CPerson& person);

void Serialize(CArchive& ar);

};

IMPLEMENT_SERIAL( CPerson, CObject, 1 )

CPerson& CPerson::operator = (CPerson&

person)

{

m_IDnumber = person.m_IDnumber;

m_strName = person.m_strName;

m_strNation = person.m_strNation;

m_nSex = person.m_nSex;

m_nAge = person.m_nAge;

m_bMarried = person.m_bMarried;

return *this;

}

void CPerson::Serialize(CArchive& ar)

{

CObject::Serialize( ar); // 首先调用基类的

Serialize()方法

if(ing())

{

ar << m_IDnumber;

ar << m_strName;

ar << m_strNation;

ar << m_nSex;

ar << m_nAge;

ar << (int)m_bMarried;

}

else

{

ar >> m_IDnumber;

ar >> m_strName;

ar >> m_strNation;

ar >> m_nSex;

ar >> m_nAge;

ar >> (int)m_bMarried;

}

}

7-21 修改例13-3的吹泡泡程序,使其打印每

个泡泡的数据值。打印格式为每页40行,页

眉为文档名,页脚为页号。

说 明:首先为视图类添加一个数据成员

m_nLinePerPage,用来存放每页行数,并在视

图类CMyView的构造函数中将

m_nLinePerPage初始化为40。

修改视图类成员函数OnPrepareDC(),设置

映射模式为MM_TWIPS。该模式为每英寸

1440点,很适合打印机输出。

程 序:

重载视图类的成员函数OnPreparePrinting(),

在其中添加计算打印页数的代码:

BOOL

CMyView::OnPreparePrinting(CPrintInfo*

pInfo)

{

CMyDoc *pDoc = GetDocument();

int nPageCount =

pDoc->GetListSize()/m_nLinePerPage;

if(pDoc->GetListSize() % m_nLinePerPage)

nPageCount ++;

pInfo->SetMaxPage(nPageCount);

return DoPreparePrinting(pInfo);

}

最后重载视图类的OnPrint()函数并添加打

印代码:

void CMyView::OnPrint( CDC* pDC,

CPrintInfo* pInfo )

{

int nPage = pInfo->m_nCurPage; // 当前页号

int nStart = (nPage-1)*m_nLinePerPage; // 本

页第一行

int nEnd = nStart+m_nLinePerPage; // 本页最

后一行

CFont font; // 设置字体

Font(-280, 0, 0, 0, 400, FALSE,

FALSE, 0, ANSI_CHARSET,

OUT_DEFAULT_PRECIS,

CLIP_DEFAULT_PRECIS,

DEFAULT_QUALITY,

DEFAULT_PITCH|FF_MODERN, "Courier

New");

CFont *pOldFont = (CFont

*)(pDC->SelectObject(&font));

CRect rectPaper = pInfo->m_rectDraw; // 取页

面打印矩形

// 页眉: 页面顶端中央打印文档名称

CMyDoc *pDoc = GetDocument();

ASSERT_VALID(pDoc);

CString str;

("Bubble Report: %s",

(LPCSTR)pDoc->GetTitle());

CSize sizeText = pDC->GetTextExtent(str);

CPoint point((()-)/2,

0);

pDC->TextOut(point.x, point.y, str);

point.x = ; // 打印页眉下划线

point.y = ;

pDC->MoveTo(point);

point.x = ;

pDC->LineTo(point);

// 打印表头

("%6.6s %6.6s %6.6s %6.6s %6.6s",

"Index", "Left", "Top", "Right", "Bottom");

point.x = 720;

point.y -= 720;

pDC->TextOut(point.x, point.y, str);

TEXTMETRIC tm; // 取当前字体有关信息

pDC->GetTextMetrics(&tm);

int nHeight =

ht+rnalLeading;

point.y -= 360; // 下移 1/4 英寸

for(int i=nStart; i

{

if(i >= pDoc->GetListSize())

break;

("%6d %6d %6d %6d %6d", i+1,

pDoc->GetBubble(i).left,

pDoc->GetBubble(i).top,

pDoc->GetBubble(i).right,

pDoc->GetBubble(i).bottom);

point.y -= nHeight;

pDC->TextOut(point.x, point.y, str);

}

// 在页面底部中央打印页号

("- %d -", nPage);

sizeText = pDC->GetTextExtent(str);

point.x = (()-)/2;

point.y = ()+;

pDC->TextOut(point.x, point.y, str);

// 释放字体对象

pDC->SelectObject(pOldFont);

}

7-22 修改例11-4的拼图程序,使之在难度

菜单的相应选项前打钩。

程 序:

首先在框架窗口类的消息响应函数声明处增

加以下消息响应函数的声明:

afx_msg void

CPuzzleWnd::OnUpdateGrad01(CCmdUI*

pCmdUI);

afx_msg void

CPuzzleWnd::OnUpdateGrad02(CCmdUI*

pCmdUI);

afx_msg void

CPuzzleWnd::OnUpdateGrad03(CCmdUI*

pCmdUI);

然后在框架窗口类的消息映射宏中加入相应

内容:

BEGIN_MESSAGE_MAP(CPuzzleWnd,

CFrameWnd)

ON_WM_LBUTTONDOWN()

ON_WM_LBUTTONUP()

ON_WM_MOUSEMOVE()

ON_WM_PAINT()

ON_COMMAND(ID_SHOWFIG, OnShowFig)

ON_COMMAND(ID_GRAD01, OnGrad01)

ON_COMMAND(ID_GRAD02, OnGrad02)

ON_COMMAND(ID_GRAD03, OnGrad03)

ON_UPDATE_COMMAND_UI(ID_GRAD01,

OnUpdateGrad01)

ON_UPDATE_COMMAND_UI(ID_GRAD02,

OnUpdateGrad02)

ON_UPDATE_COMMAND_UI(ID_GRAD03,

OnUpdateGrad03)

END_MESSAGE_MAP()

注意更新命令用户接口消息映射宏将菜单标

识符与相应的消息映射函数联系在一起。最后

编写相应的更新命令用户接口消息映射函数:

void CPuzzleWnd::OnUpdateGrad01(CCmdUI*

pCmdUI) {

pCmdUI->SetCheck(m_nColCount == 4);

}

void CPuzzleWnd::OnUpdateGrad02(CCmdUI*

pCmdUI) {

pCmdUI->SetCheck(m_nColCount == 8);

}

void CPuzzleWnd::OnUpdateGrad03(CCmdUI*

pCmdUI) {

pCmdUI->SetCheck(m_nColCount == 16);

}

输入输出:在选择拼图难度时,可在相应选项

前打钩(图13-4)。

// Example 13-7: 七巧板程序

//////////////////////////////////

#include

#include

// 拼板类 ////////////////////////////////////////////////////

#define MAX_POINTS 4

#define CHIP_WIDTH 240

#define DELTA 30

class CChip : public CObject

{

DECLARE_SERIAL(CChip)

int m_nType;

CPoint m_pointList[MAX_POINTS];

int m_nPointCount;

public:

CChip(){}

void SetChip(int type, POINT *ppointlist, int

count);

void DrawChip(CDC *pDC);

BOOL PtInChip(POINT point);

LPCRECT GetRect();

void MoveTo(CSize offset);

void Rotation();

void Serialize(CArchive &ar);

};

IMPLEMENT_SERIAL(CChip, CObject, 1)

// 设置拼图块参数

void CChip::SetChip(int type, POINT

*ppointlist, int count)

{

m_nType = type;

m_nPointCount = count;

for(int i=0; i

m_pointList[i] = ppointlist[i];

}

// 绘出拼图块

void CChip::DrawChip(CDC *pDC)

{

CPen penNew, *ppenOld;

CBrush brushNew, *pbrushOld;

switch(m_nType)

{

case 1: SolidBrush(RGB(127,

127, 127));

break;

case 2: SolidBrush(RGB(255,

0, 0));

break;

case 3: SolidBrush(RGB(0,

255, 0));

break;

case 4: SolidBrush(RGB(0, 0,

255));

break;

case 5: SolidBrush(RGB(127,

127, 0));

break;

case 6: SolidBrush(RGB(127,

0, 127));

break;

case 7: SolidBrush(RGB(0,

127, 127));

break;

}

Pen(PS_SOLID, 1, RGB(0, 0,

0));

ppenOld = pDC->SelectObject(&penNew);

pbrushOld = pDC->SelectObject(&brushNew);

pDC->Polygon(m_pointList, m_nPointCount);

pDC->SelectObject(ppenOld);

pDC->SelectObject(pbrushOld);

}

// 检测一点是否在拼图块中

BOOL CChip::PtInChip(POINT point)

{

CRgn rgn;

PolygonRgn(m_pointList,

m_nPointCount, 0);

return gion(point);

}

// 取拼图块的包含矩形

LPCRECT CChip::GetRect()

{

static RECT rect;

CRgn rgn;

PolygonRgn(m_pointList,

m_nPointCount, 0);

Box(&rect);

++;

++;

return ▭

}

// 旋转拼图块

void CChip::Rotation()

{

CRect rect;

CRgn rgn;

PolygonRgn(m_pointList,

m_nPointCount, 0);

Box(&rect);

double x = +()/2; // 计算旋转

中心

double y = +()/2;

double dx, dy;

for(int i=0; i

{

dx = m_pointList[i].x-x;

dy = m_pointList[i].y-y;

m_pointList[i].x =

(int)(x+dx*0.7071-dy*0.7071);

m_pointList[i].y =

(int)(y+dx*0.7071+dy*0.7071);

}

}

// 移动拼图块

void CChip::MoveTo(CSize offset)

{

for(int i=0; i

m_pointList[i] = m_pointList[i]+offset;

}

// 序列化

void CChip::Serialize(CArchive &ar)

{

if(ing())

{

ar << m_nType;

ar << m_nPointCount;

for(int i=0; i

ar << m_pointList[i];

}

else

{

ar >> m_nType;

ar >> m_nPointCount;

for(int i=0; i

ar >> m_pointList[i];

}

}

// 文档类 ////////////////////////////////////////////////////

#define CHIP_COUNT 7

class CMyDoc : public CDocument

{

DECLARE_DYNCREATE(CMyDoc)

CChip m_chipList[CHIP_COUNT];

public:

void Reset();

virtual void DeleteContents();

virtual void Serialize(CArchive& ar);

};

IMPLEMENT_DYNCREATE(CMyDoc,

CDocument)

// 初始化拼图块

void CMyDoc::Reset()

{

POINT pointList[MAX_POINTS];

pointList[0].x = DELTA;

pointList[0].y = DELTA;

pointList[1].x = DELTA+CHIP_WIDTH;

pointList[1].y = DELTA;

pointList[2].x = DELTA+CHIP_WIDTH/2;

pointList[2].y = DELTA+CHIP_WIDTH/2;

m_chipList[0].SetChip(1, pointList, 3);

pointList[0].x = DELTA;

pointList[0].y = DELTA;

pointList[1].x = DELTA;

pointList[1].y = DELTA+CHIP_WIDTH;

pointList[2].x = DELTA+CHIP_WIDTH/2;

pointList[2].y = DELTA+CHIP_WIDTH/2;

m_chipList[1].SetChip(2, pointList, 3);

pointList[0].x = DELTA+CHIP_WIDTH;

pointList[0].y = DELTA;

pointList[1].x = DELTA+CHIP_WIDTH;

pointList[1].y = DELTA+CHIP_WIDTH/2;

pointList[2].x = DELTA+(CHIP_WIDTH*3)/4;

pointList[2].y = DELTA+CHIP_WIDTH/4;

m_chipList[2].SetChip(3, pointList, 3);

pointList[0].x = DELTA+CHIP_WIDTH/2;

pointList[0].y = DELTA+CHIP_WIDTH/2;

pointList[1].x = DELTA+CHIP_WIDTH/4;

pointList[1].y = DELTA+(CHIP_WIDTH*3)/4;

pointList[2].x = DELTA+(CHIP_WIDTH*3)/4;

pointList[2].y = DELTA+(CHIP_WIDTH*3)/4;

m_chipList[3].SetChip(4, pointList, 3);

pointList[0].x = DELTA+CHIP_WIDTH;

pointList[0].y = DELTA+CHIP_WIDTH/2;

pointList[1].x = DELTA+CHIP_WIDTH;

pointList[1].y = DELTA+CHIP_WIDTH;

pointList[2].x = DELTA+CHIP_WIDTH/2;

pointList[2].y = DELTA+CHIP_WIDTH;

m_chipList[4].SetChip(5, pointList, 3);

pointList[0].x = DELTA+(CHIP_WIDTH*3)/4;

pointList[0].y = DELTA+CHIP_WIDTH/4;

pointList[1].x = DELTA+CHIP_WIDTH/2;

pointList[1].y = DELTA+CHIP_WIDTH/2;

pointList[2].x = DELTA+(CHIP_WIDTH*3)/4;

pointList[2].y = DELTA+(CHIP_WIDTH*3)/4;

pointList[3].x = DELTA+CHIP_WIDTH;

pointList[3].y = DELTA+CHIP_WIDTH/2;

m_chipList[5].SetChip(6, pointList, 4);

pointList[0].x = DELTA;

pointList[0].y = DELTA+CHIP_WIDTH;

pointList[1].x = DELTA+CHIP_WIDTH/4;

pointList[1].y = DELTA+(CHIP_WIDTH*3)/4;

pointList[2].x = DELTA+(CHIP_WIDTH*3)/4;

pointList[2].y = DELTA+(CHIP_WIDTH*3)/4;

pointLis ¨t[3].x = DELTA+CHIP_WIDTH/2;

pointList[3].y = DELTA+CHIP_WIDTH;

m_chipList[6].SetChip(7, pointList, 4); // 清理

文档:关闭文档、建立新文档和打开文档前调

void CMyDoc::DeleteContents() {

Reset();

CDocument::DeleteContents();

}

// 系列化:读写文档时自动调用

void CMyDoc::Serialize(CArchive &ar)

{

for(int i=0; i

m_chipList[i].Serialize(ar);

}

// 视图类 ///////////////////////////////////////////////////

class CMyView : public CView

{

DECLARE_DYNCREATE(CMyView)

BOOL m_bCaptured;

CPoint m_pointMouse;

int m_nCurrIndex;

public:

CMyView(){m_bCaptured = FALSE;}

CMyDoc* GetDocument(){return

(CMyDoc*)m_pDocument;}

virtual void OnInitialUpdate();

virtual BOOL OnPreparePrinting(CPrintInfo*

pInfo);

virtual void OnDraw(CDC* pDC);

afx_msg void OnLButtonDown(UINT nFlags,

CPoint point);

afx_msg void OnLButtonUp(UINT nFlags,

CPoint point);

afx_msg void OnMouseMove(UINT nFlags,

CPoint point);

afx_msg void OnRButtonDown(UINT nFlags,

CPoint point);

DECLARE_MESSAGE_MAP()

};

IMPLEMENT_DYNCREATE(CMyView,

CView)

BEGIN_MESSAGE_MAP(CMyView, CView)

ON_WM_LBUTTONDOWN()

ON_WM_LBUTTONUP()

ON_WM_MOUSEMOVE()

ON_WM_RBUTTONDOWN()

ON_COMMAND(ID_FILE_PRINT,

CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_DIRECT,

CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_PREVIEW,

CView::OnFilePrintPreview)

END_MESSAGE_MAP()

// 更新初始化:当建立新文档或打开文档时

调用

void CMyView::OnInitialUpdate()

{

CView::OnInitialUpdate();

Invalidate();

}

// 绘制视图:程序开始运行或窗体发生变化

时自动调用

void CMyView::OnDraw(CDC* pDC)

{

CMyDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

for(int i=0; i

pDoc->m_chipList[i].DrawChip(pDC);

}

// 消息响应:用户点击鼠标左键时调用

void CMyView::OnLButtonDown(UINT nFlags,

CPoint point)

{

CMyDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

for(int i=CHIP_COUNT-1; i>=0; i--)

if(pDoc->m_chipList[i].PtInChip(point))

{

SetCapture();

m_bCaptured = TRUE;

m_pointMouse = point;

m_nCurrIndex = i;

break;

}

}

// 释放鼠标左键

void CMyView::OnLButtonUp(UINT nFlags,

CPoint point)

{

if(m_bCaptured)

{

::ReleaseCapture();

m_bCaptured = FALSE;

}

}

// 移动鼠标左键

void CMyView::OnMouseMove(UINT nFlags,

CPoint point)

{

if(m_bCaptured)

{

CMyDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

InvalidateRect(pDoc->m_chipList[m_nCurrInde

x].GetRect());

CSize offset(point-m_pointMouse);

pDoc->m_chipList[m_nCurrIndex].MoveTo(off

set);

InvalidateRect(pDoc->m_chipList[m_nCurrInde

x].GetRect());

m_pointMouse = point;

pDoc->SetModifiedFlag();

}

}

// 按下鼠标右键: 旋转拼图块

void CMyView::OnRButtonDown(UINT nFlags,

CPoint point)

{

CMyDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

for(int i=CHIP_COUNT-1; i>=0; i--)

if(pDoc->m_chipList[i].PtInChip(point))

{

InvalidateRect(pDoc->m_chipList[i].GetRect())

;

pDoc->m_chipList[i].Rotation();

InvalidateRect(pDoc->m_chipList[i].GetRect(),

FALSE);

pDoc->SetModifiedFlag();

break;

}

}

// 准备打印:设置打印参数

BOOL

CMyView::OnPreparePrinting(CPrintInfo*

pInfo)

{

pInfo->SetMaxPage(1);

return DoPreparePrinting(pInfo);

}

// 主框架类 //////////////////////////////////////////////////

class CMainFrame : public CFrameWnd

{

DECLARE_DYNCREATE(CMainFrame)

};

IMPLEMENT_DYNCREATE(CMainFrame,

CFrameWnd)

// 应用程序类

///////////////////////////////////////////////

#define IDR_MAINFRAME 128 // 主框架的

资源代号

class CMyApp : public CWinApp

{

public:

virtual BOOL InitInstance();

DECLARE_MESSAGE_MAP()

};

BEGIN_MESSAGE_MAP(CMyApp,

CWinApp)

ON_COMMAND(ID_FILE_NEW,

CWinApp::OnFileNew)

ON_COMMAND(ID_FILE_OPEN,

CWinApp::OnFileOpen)

ON_COMMAND(ID_FILE_PRINT_SETUP,

CWinApp::OnFilePrintSetup)

END_MESSAGE_MAP()

// 初始化程序实例:建立并登记文档模板

BOOL CMyApp::InitInstance()

{

CSingleDocTemplate* pDocTemplate;

pDocTemplate = new CSingleDocTemplate(

IDR_MAINFRAME,

RUNTIME_CLASS(CMyDoc),

RUNTIME_CLASS(CMainFrame),

RUNTIME_CLASS(CMyView));

AddDocTemplate(pDocTemplate);

CCommandLineInfo cmdInfo;

ParseCommandLine(cmdInfo);

if (!ProcessShellCommand(cmdInfo))

return FALSE;

m_pMainWnd->ShowWindow(SW_SHOWMA

XIMIZED);

return TRUE;

}

// 全局应用程序对象

CMyApp theApp;

7-23 为例9-3的吹泡泡程序添加一标识符为

IDI_MAINICON的图标(该图标应已按11.8:

“向项目中添加资源”中的方法建立并加入项

目)。

说 明:建立项目的方法见9.8:“用Visual C++

集成开发环境开发Win32应用程序”。

程 序:

在例9-3程序前面添加一文件包含命令:

#include ”resource.h”

并将CMyApp::InitInstance()函数修改为:

BOOL CMyApp::InitInstance()

{

HICON hIcon;

hIcon = LoadIcon(IDI_MAINICON); // 载入图

CMyWnd *pFrame = new CMyWnd;

pFrame->Create(0,_T("吹泡泡程序"));

pFrame->SetIcon(hIcon, TRUE); // 设置大图

pFrame->SetIcon(hIcon, FALSE); // 设置小图

pFrame->ShowWindow(m_nCmdShow);

this->m_pMainWnd = pFrame;

return TRUE;

}

7-24 显示一张位图文件(.BMP)。

说 明:首先建立Win32 Application空白项目

和源代码文件(不要忘记设置项目使之可以使

用MFC类库),然后按11.8:“向项目中添加

资源”的方法为项目建立资源文件,并将待显

示的位图文件作为资源装入项目。

程 序:

// Example 11-2:显示BMP图片

#include

#include "resource.h"

// 框架窗口类

class CMyWnd: public CFrameWnd

{

CBitmap m_Bitmap;

int m_nHeight;

int m_nWidth;

public:

CMyWnd();

protected:

afx_msg void OnPaint();

DECLARE_MESSAGE_MAP()

};

// 消息映射

BEGIN_MESSAGE_MAP(CMyWnd,

CFrameWnd)

ON_WM_PAINT()

END_MESSAGE_MAP()

// 框架窗口类的成员函数

CMyWnd::CMyWnd()

{

m_tmap(IDB_BITMAP1);

BITMAP BM;

m_map(&BM);

m_nWidth = h;

m_nHeight = ht;

}

// 响应绘制窗口客户区消息

void CMyWnd::OnPaint()

{

CPaintDC dc(this);

CDC MemDC;

CompatibleDC(NULL);

Object(&m_Bitmap);

(0,0,m_nWidth,m_nHeight,&MemDC,

0,0,SRCCOPY);

}

// 应用程序类

class CMinMFCApp: public CWinApp

{

public:

BOOL InitInstance();

};

// 应用程序类的成员函数

// 初始化应用程序实例

BOOL CMinMFCApp::InitInstance()

{

CMyWnd *pFrame = new CMyWnd;

pFrame->Create(0,_T("Beautiful Cats”));

pFrame->ShowWindow(m_nCmdShow);

this->m_pMainWnd = pFrame;

return TRUE;

}

// 全局应用程序对象

CMinMFCApp ThisApp;

7-25 修改11-2,使之可以不同比例放大或缩

小图象。

说 明:使用StretchBlt()函数代替BitBlt()

函数就可实现图象的缩放显示。在项目中加入

一个弹出式菜单(将标识符改为

IDR_MAINMENU),内含3个菜单选项:缩

小1倍显示,按原尺寸显示和放大1倍显示,

其标识符分别改为ID_SHRINK,ID_BESTFIT

和ID_ZOOMOUT。

程 序:

// Example 11-3:以不同尺寸显示BMP图片

#include

#include "resource.h"

// 框架窗口类

class CMyWnd: public CFrameWnd

{

CBitmap m_Bitmap;

float m_fTimes;

int m_nHeight;

int m_nWidth;

public:

CMyWnd();

BOOL PreCreateWindow(CREATESTRUCT

&cs);

protected:

afx_msg void OnPaint();

afx_msg void OnShrink();

afx_msg void OnBestFit();

afx_msg void OnZoomOut();

DECLARE_MESSAGE_MAP()

};

// 消息映射

BEGIN_MESSAGE_MAP(CMyWnd,

CFrameWnd)

ON_WM_PAINT()

ON_COMMAND(ID_SHRINK, OnShrink)

ON_COMMAND(ID_BESTFIT, OnBestFit)

ON_COMMAND(ID_ZOOMOUT,

OnZoomOut)

END_MESSAGE_MAP()

// 主窗口类的成员函数

CMyWnd::CMyWnd()

{

BITMAP BM;

m_tmap(IDB_BITMAP1);

m_map(&BM);

m_nWidth = h;

m_nHeight = ht;

m_fTimes = 1.0;

}

// 装入菜单

BOOL

CMyWnd::PreCreateWindow(CREATESTRUC

T &cs)

{

= LoadMenu(NULL,

MAKEINTRESOURCE(IDR_MAINMENU));

return CFrameWnd::PreCreateWindow(cs);

}

// 缩小图象

void CMyWnd::OnShrink()

{

m_fTimes = 0.5;

Invalidate();

}

// 放大图象

void CMyWnd::OnZoomOut()

{

m_fTimes = 2.0;

Invalidate();

}

// 原样显示

void CMyWnd::OnBestFit()

{

m_fTimes = 1.0;

Invalidate();

}

// 响应绘制窗口客户区消息

void CMyWnd::OnPaint()

{

CPaintDC dc(this);

CDC MemDC;

CompatibleDC(NULL);

Object(&m_Bitmap);

hBlt(0, 0, (int)(m_nWidth*m_fTimes),

(int)(m_nHeight*m_fTimes),

&MemDC, 0, 0, m_nWidth, m_nHeight,

SRCCOPY);

}

// 应用程序类

class CMyApp: public CWinApp

{

public:

BOOL InitInstance();

};

// 应用窗口类的成员函数

// 初始化应用程序实例

BOOL CMyApp::InitInstance()

{

CMyWnd *pFrame = new CMyWnd;

pFrame->Create(0,_T("Show Bitmap 1.0"));

pFrame->ShowWindow(SW_SHOWMAXIMIZ

ED);

pFrame->UpdateWindow();

this->m_pMainWnd = pFrame;

return TRUE;

}

// 全局应用程序对象

CMyApp ThisApp;

7-26 拼图程序。

设计思想:将一张图片切分成若干小片,打乱

顺序任意显示。用户可用鼠标拖动各小片到正

确位置以恢复用来的图象。

说 明:首先创建一Win32 Application空项目,

然后向项目中添加C++ Source File(源程序)

和Resource Script(资源描述)文件。在添加

资源描述文件后应将其关闭。

按本单元介绍的有关内容为本程序添加图标、

字符串(窗口标题)和下拉菜单(标题为“游

戏”)等资源,其标识符均取

IDR_MAINFRAME。

为“游戏”下拉菜单添4个菜单选项,一个是

“自动拼图”, 和一个“结束”,标识符分别

为ID_BEGIN和ID_END。剩下3个用来控制

拼图的难度,标识符分别为ID_GRAD01,

ID_GRAD02和ID_GRAD03。

另选一幅漂亮的图片,作为资源装入项目。该

图片即拼图的底图。然后输入以下程序,注意

在编译前选择使用MFC。

程 序:

// Example 11-4: 拼图程序

#include

#include

#include

#include

#include "resource.h"

#define MAX_CHIPS 500

// 派生一个框架窗口类

class CPuzzleWnd: public CFrameWnd

{

CBitmap m_bmpPuzzle; // 位图

int m_nPuzzleWidth; // 位图宽

int m_nPuzzleHeight; // 位图高

int m_nColCount; // 每行拼图块数

int m_nRowCount; // 每列拼图块数

CRect m_rectChips[MAX_CHIPS]; // 每个拼

图块的位置

int m_nChipWidth; // 拼图块宽

int m_nChipHeight; // 拼图块高

BOOL m_bCaptured;

CPoint m_pointMouse;

int m_nCurrIndex;

public:

CPuzzleWnd();

void InitPuzzle(int colcount, int rowcount);

protected:

afx_msg void OnLButtonDown(UINT nFlags,

CPoint point);

afx_msg void OnLButtonUp(UINT nFlags,

CPoint point);

afx_msg void OnMouseMove(UINT nFlags,

CPoint point);

afx_msg void OnPaint();

afx_msg void OnShowFig();

afx_msg void OnGrad01();

afx_msg void OnGrad02();

afx_msg void OnGrad03();

DECLARE_MESSAGE_MAP()

};

// 消息映射

BEGIN_MESSAGE_MAP(CPuzzleWnd,

CFrameWnd)

ON_WM_LBUTTONDOWN()

ON_WM_LBUTTONUP()

ON_WM_MOUSEMOVE()

ON_WM_PAINT()

ON_COMMAND(ID_SHOWFIG, OnShowFig)

ON_COMMAND(ID_GRAD01, OnGrad01)

ON_COMMAND(ID_GRAD02, OnGrad02)

ON_COMMAND(ID_GRAD03, OnGrad03)

END_MESSAGE_MAP()

// 主窗口类的成员函数

// 构造函数: 装入位图资源

CPuzzleWnd::CPuzzleWnd()

{

BITMAP BM;

m_tmap(IDB_BITMAP1);

m_ect(sizeof(BM), &BM);

m_nPuzzleWidth = h;

m_nPuzzleHeight = ht;

InitPuzzle(4, 3);

m_bCaptured = FALSE;

}

// 初始化拼图数组

void CPuzzleWnd::InitPuzzle(int colcount, int

rowcount)

{

m_nColCount = colcount;

m_nRowCount = rowcount;

m_nChipWidth =

m_nPuzzleWidth/m_nColCount;

m_nChipHeight =

m_nPuzzleHeight/m_nRowCount;

srand((unsigned)time( NULL));

for(int row=0; row

for(int col=0; col

{

int index = row*m_nColCount+col;

m_rectChips[index].left =

rand()%m_nPuzzleWidth+20;

m_rectChips[index].top =

rand()%m_nPuzzleHeight+20;

m_rectChips[index].right =

m_rectChips[index].left

+m_nChipWidth;

m_rectChips[index].bottom =

m_rectChips[index].top

+m_nChipHeight;

}

}

// 自动完成拼图

void CPuzzleWnd::OnShowFig()

{

for(int row=0; row

for(int col=0; col

{

int index = row*m_nColCount+col;

m_rectChips[index].left = col*m_nChipWidth;

m_rectChips[index].top = row*m_nChipHeight;

m_rectChips[index].right =

m_rectChips[index].left

+m_nChipWidth;

m_rectChips[index].bottom =

m_rectChips[index].top

+m_nChipHeight;

}

Invalidate();

}

// 设置一级难度

void CPuzzleWnd::OnGrad01()

{

InitPuzzle(4, 3);

Invalidate();

}

// 设置二级难度

void CPuzzleWnd::OnGrad02()

{

InitPuzzle(8, 6);

Invalidate();

}

// 设置三级难度

void CPuzzleWnd::OnGrad03()

{

InitPuzzle(16, 12);

Invalidate();

}

// 按下鼠标左键 void

CPuzzleWnd::OnLButtonDown(UINT nFlags,

CPoint point)

{

for(int row=m_nRowCount-1; row>=0; row--)

for(int col=m_nColCount-1; col>=0; col--)

{

int index = row*m_nColCount+col;

if(m_rectChips[index].PtInRect(point))

{

SetCapture();

m_bCaptured = TRUE;

m_pointMouse = point;

m_nCurrIndex = index;

return;

}

}

}

// 释放鼠标左键

void CPuzzleWnd::OnLButtonUp(UINT nFlags,

CPoint point)

{

if(m_bCaptured)

{

::ReleaseCapture();

m_bCaptured = FALSE;

}

}

// 移动鼠标左键

void CPuzzleWnd::OnMouseMove(UINT

nFlags, CPoint point)

{

if(m_bCaptured)

{

InvalidateRect(m_rectChips[m_nCurrIndex]);

CSize offset(point-m_pointMouse);

m_rectChips[m_nCurrIndex] += offset;

InvalidateRect(m_rectChips[m_nCurrIndex],

FALSE);

m_pointMouse = point;

}

}

// 显示当前拼图

void CPuzzleWnd::OnPaint()

{

CPaintDC dc(this);

CDC MemDC;

CompatibleDC(NULL);

Object(&m_bmpPuzzle);

for(int row=0; row

for(int col=0; col

{

int index = row*m_nColCount+col;

(m_rectChips[index].left,

m_rectChips[index].top,

m_nChipWidth,

m_nChipHeight,

&MemDC,

col*m_nChipWidth,

row*m_nChipHeight,

SRCCOPY);

}

}

// 应用程序类

class CPuzzleApp: public CWinApp

{

public:

BOOL InitInstance();

};

// 初始化应用程序实例

BOOL CPuzzleApp::InitInstance()

{

CPuzzleWnd *pFrame = new CPuzzleWnd;

pFrame->LoadFrame(IDR_MAINMENU);

pFrame->ShowWindow(SW_SHOWMAXIMIZ

ED);

this->m_pMainWnd = pFrame;

return TRUE;

}

// 全局应用程序对象

CPuzzleApp TheApp;

// Example 11-5: 地空战游戏程序

#include

#include "resource.h"

// 定义飞机类

class CPlane: public CObject

{

CPoint m_pointPlane; // 飞机位置

CBitmap m_bmpPlane; // 飞机图象

int m_nWidth; // 飞机图象宽

int m_nHeight; // 飞机图象高

public:

CPlane();

void ShowPlane(CDC *pDC, CDC *pMemDC,

CRect Client);

CRect GetPlane(){return CRect(m_pointPlane.x,

m_pointPlane.y,

m_pointPlane.x+m_nWidth,

m_pointPlane.y+m_nHeight);}

void ChangePos();

void ResetPos(){m_pointPlane.x = 0;}

};

// 飞机类的成员函数

// 构造函数

CPlane::CPlane()

{

m_pointPlane = CPoint(0, 50);

m_tmap(IDB_PLANE);

BITMAP BM;

m_map(&BM);

m_nWidth = h;

m_nHeight = ht;

}

// 显示飞机

void CPlane::ShowPlane(CDC *pDC, CDC

*pMemDC, CRect Client)

{

pMemDC->SelectObject(&m_bmpPlane);

pDC->BitBlt(m_pointPlane.x, m_pointPlane.y,

m_nWidth, m_nHeight,

pMemDC,0,0,SRCAND);

}

// 改变飞机位置

void CPlane::ChangePos()

{

if(m_pointPlane.x>788)

m_pointPlane.x = 0;

else

m_pointPlane.x += 10;

}

// 定义炸弹类

class CBomb: public CObject

{

CPoint m_pointBomb; // 炸弹位置

CBitmap m_bmpBomb; // 炸弹图象

int m_nWidth; // 炸弹图象高

int m_nHeight; // 炸弹图象宽

public:

CBomb();

void ShowBomb(CDC *pDC, CDC *pMemDC,

CRect Client);

CRect GetBomb(){return

CRect(m_pointBomb.x, m_pointBomb.y,

m_pointBomb.x+m_nWidth,

m_pointBomb.y+m_nHeight);}

void ChangePos(int x);

void ResetPos(){m_pointBomb.x=0,

m_pointBomb.y=80;}

};

// 炸弹类成员函数

// 炸弹类构造函数

CBomb::CBomb()

{

m_pointBomb.x = 0;

m_pointBomb.y = 80;

m_tmap(IDB_BOMB);

BITMAP BM;

m_map(&BM);

m_nWidth = h;

m_nHeight = ht;

}

// 显示炸弹

void CBomb::ShowBomb(CDC *pDC, CDC

*pMemDC, CRect Client)

{

pMemDC->SelectObject(&m_bmpBomb);

pDC->BitBlt(m_pointBomb.x, m_pointBomb.y,

m_nWidth, m_nHeight, pMemDC, 0, 0,

SRCAND);

}

// 改变位置

void CBomb::ChangePos(int x)

{

m_pointBomb.y += 20;

if(m_pointBomb.y>480)

m_pointBomb.y = 80;

m_pointBomb.x = x;

}

// 定义高炮类

class CTank: public CObject

{

CPoint m_pointTank; // 高炮位置

CBitmap m_bmpTank; // 高炮图象

int m_nWidth; // 高炮图象宽

int m_nHeight; // 高炮图象高

public:

CTank();

void ShowTank(CDC *pDC, CDC *pMemDC,

CRect Client);

CRect GetTank(){return CRect(m_pointTank.x,

m_pointTank.y,

m_pointTank.x+m_nWidth,

m_pointTank.y+m_nHeight);}

void ChangePos(int tag);

void ResetPos(){m_pointTank.x = 350;}

};

// 高炮类成员函数

// 高炮类构造函数

CTank::CTank()

{

m_pointTank.x = 350;

m_pointTank.y = 450;

m_tmap(IDB_TANK);

BITMAP BM;

m_map(&BM);

m_nWidth = h;

m_nHeight = ht;

}

// 显示高炮

void CTank::ShowTank(CDC *pDC, CDC

*pMemDC, CRect Client)

{

pMemDC->SelectObject(&m_bmpTank);

pDC->BitBlt(m_pointTank.x, m_pointTank.y,

m_nWidth, m_nHeight,

pMemDC,0,0,SRCAND);

}

// 改变位置

void CTank::ChangePos(int tag)

{

if(tag == 0 && m_pointTank.x > 0)

m_pointTank.x -= 20;

else if(tag == 1 && m_pointTank.x+m_nWidth

< 798)

m_pointTank.x += 20;

}

// 定义炮弹类

class CStone: public CObject

{

CPoint m_pointStone; // 炮弹位置

CBitmap m_bmpStone; // 炮弹图象

int m_nWidth; // 炮弹图象宽

int m_nHeight; // 炮弹图象高

BOOL m_bShot; // 是否已发射

public:

CStone();

BOOL HaveStone(){return !m_bShot;}

void Shot(int x);

void ShowStone(CDC *pDC, CDC *pMemDC,

CRect Client);

CRect GetStone(){return CRect(m_pointStone.x,

m_pointStone.y,

m_pointStone.x+m_nWidth,

m_pointStone.y+m_nHeight);}

void ChangePos();

void ResetPos(){m_bShot = FALSE;}

void Refill(){m_bShot = FALSE;}

};

// 炮弹类成员函数

// 炮弹类构造函数

CStone::CStone()

{

m_bShot = FALSE;

m_tmap(IDB_STONE);

BITMAP BM;

m_map(&BM);

m_nWidth = h;

m_nHeight = ht;

}

// 发射

void CStone::Shot(int x)

{

m_bShot = TRUE;

m_pointStone.x = x+48;

m_pointStone.y = 440;

}

// 显示炮弹

void CStone::ShowStone(CDC *pDC, CDC

*pMemDC, CRect Client)

{

if(m_bShot)

{

pMemDC->SelectObject(&m_bmpStone);

pDC->BitBlt(m_pointStone.x, m_pointStone.y,

m_nWidth, m_nHeight,

pMemDC,0,0,SRCAND);

}

}

// 改变位置

void CStone::ChangePos()

{

if(m_bShot)

m_pointStone.y -= 20;

}

// 自定义消息

#define ON_WM_GAMEOVER 0x0402

#define I_SHOT_YOU 0

#define YOU_SHOT_ME 1

// 定义框架窗口类

class CMyWnd: public CFrameWnd

{

CPlane m_Plane;

CBomb m_Bomb;

CTank m_Tank;

CStone m_Stone;

CPoint m_pointMoutain[5];

public:

CMyWnd();

void DrawFields(CDC *pDC, CRect Client);

BOOL ShotOn(CRect &body1, CRect &body2);

protected:

afx_msg void OnPaint();

afx_msg void OnBegin();

afx_msg void OnEnd();

afx_msg void OnTimer(UINT nIDEvent);

afx_msg void OnKeyDown(UINT nChar, UINT

nRepCnt, UINT nFlags);

afx_msg void OnGameOver(UINT tag);

DECLARE_MESSAGE_MAP()

};

// 消息映射

BEGIN_MESSAGE_MAP(CMyWnd,CFrame

Wnd)

ON_WM_PAINT()

ON_COMMAND(ID_BEGIN, OnBegin)

ON_COMMAND(ID_END, OnEnd)

ON_WM_TIMER()

ON_WM_KEYDOWN()

ON_MESSAGE(ON_WM_GAMEOVER,

OnGameOver)

END_MESSAGE_MAP()

// 框架窗口类的成员函数

// 构造函数

CMyWnd::CMyWnd()

{

m_pointMoutain[0] = CPoint(300, 400);

m_pointMoutain[1] = CPoint(400, 300);

m_pointMoutain[2] = CPoint(500, 350);

m_pointMoutain[3] = CPoint(600, 250);

m_pointMoutain[4] = CPoint(800, 400);

}

// 更新窗口客户区

void CMyWnd::OnPaint()

{

CPaintDC dc(this);

CDC MemDC;

CompatibleDC(NULL);

CRect rect;

GetClientRect(&rect);

DrawFields(&dc, rect); // 画战场

m_ane(&dc, &MemDC, rect); //

画对方飞机

m_mb(&dc, &MemDC, rect); //

画炸弹

m_nk(&dc, &MemDC, rect); // 画

己方防空导弹车

m_one(&dc, &MemDC, rect); //

画导弹 }

// 响应菜单消息: 开始游戏

void CMyWnd::OnBegin()

{

m_os();

m_os();

m_os();

m_os();

Invalidate();

SetTimer(1, 100, NULL);

}

// 响应菜单消息: 游戏结束

void CMyWnd::OnEnd()

{

KillTimer(1);

PostMessage(WM_QUIT);

}

// 定时器消息响应函数

void CMyWnd::OnTimer(UINT nIDEvent)

{

InvalidateRect(m_ne()); // 修改飞

机位置

m_Pos();

InvalidateRect(m_ne(), FALSE);

InvalidateRect(m_b()); // 修改

炸弹位置

m_Pos(m_ne().left);

InvalidateRect(m_b(), FALSE);

if(!m_one()) // 修改炮弹位置

{

CRect rect = m_ne();

if( < 3)

m_();

else

{

InvalidateRect(rect);

m_Pos();

}

InvalidateRect(m_ne(), FALSE);

}

// 判断射击效果

if(!m_one() &&

ShotOn(m_ne(),

m_ne()))

SendMessage(ON_WM_GAMEOVER,

YOU_SHOT_ME);

else if(ShotOn(m_k(),

m_b()))

SendMessage(ON_WM_GAMEOVER,

I_SHOT_YOU);

}

// 自定义的游戏结束消息响应函数

void CMyWnd::OnGameOver(UINT tag)

{

KillTimer(1);

if(tag == I_SHOT_YOU)

MessageBox("我砸到你了");

else

MessageBox("你击中我了");

}

// 按键消息响应函数

void CMyWnd::OnKeyDown(UINT nChar,

UINT nRepCnt, UINT nFlags)

{

if(nChar == VK_LEFT) // 使用 < 键左移高

{

InvalidateRect(m_k());

m_Pos(0);

InvalidateRect(m_k(), FALSE);

}

else if(nChar == VK_RIGHT) // 使用 > 键右

移高炮

{

InvalidateRect(m_k());

m_Pos(1);

InvalidateRect(m_k());

}

else if(nChar == 32 && m_one())

// 使用空格键发炮

m_(m_k().left); }

// 画战场

void CMyWnd::DrawFields(CDC *pDC, CRect

Client)

{

CBrush *pOldBrush, brushSky, brushGrass,

brushMoutain;

CRect rect(Client);

SolidBrush(RGB(127, 200,

255)); // 画天空

pOldBrush = pDC->SelectObject(&brushSky);

pDC->Rectangle(rect);

pDC->SelectObject(pOldBrush);

SolidBrush(RGB(0, 255, 0));

// 画草地

pOldBrush =

pDC->SelectObject(&brushGrass);

= 400;

pDC->Rectangle(rect);

pDC->SelectObject(pOldBrush);

SolidBrush(RGB(125, 50,

0)); // 画大山

pOldBrush =

pDC->SelectObject(&brushMoutain);

pDC->Polygon(m_pointMoutain, 5);

pDC->SelectObject(pOldBrush);

}

// 判断射击效果 BOOL

CMyWnd::ShotOn(CRect &body1, CRect

&body2)

{

return ct(t());

}

// 定义应用程序类

class CMyApp: public CWinApp

{

public:

BOOL InitInstance();

};

// 初始化实例函数

BOOL CMyApp::InitInstance()

{

CMyWnd *pFrame = new CMyWnd;

pFrame->LoadFrame(IDR_MAINFRAME);

pFrame->ShowWindow(SW_SHOWMAXIMIZ

ED);

this->m_pMainWnd = pFrame;

return TRUE;

}

// 说明应用程序类的全局对象

CMyApp ThisApp;

第 八 章 多态性

8-1 什么叫做多态性 ?在C++中是如何实现

多态的?

解: 多态是指同样的消息被不同类型的对象

接收时导致完全不同的行为,是对类的特定成

员函数的再抽象。C++支持的多态有多种类

型,重载(包括函数重载和运算符重载)和虚函

数是其中主要的方式。 8-2 什么叫做抽象

类?抽象类有何作用?抽象类的派生类是否

一定要给出纯虚函数的实现?

解: 带有纯虚函数的类是抽象类。抽象类的

主要作用是通过它为一个类族建立一个公共

的接口,使它们能够更有效地发挥多态特性。

抽象类声明了一组派生类共同操作接口的通

用语义,而接口的完整实现,即纯虚函数的函

数体,要由派生类自己给出。但抽象类的派生

类并非一定要给出纯虚函数的实现,如果派生

类没有给出纯虚函数的实现,这个派生类仍然

是一个抽象类。

8-3 声明一个参数为整型,无返回值,名为fn1

的虚函数。

解: virtual void fn1( int );

8-4 在C++中,能否声明虚构造函数?为什

么?能否声明虚析构函数?有何用途?

解: 在C++中,不能声明虚构造函数,多态

是不同的对象对同一消息有不同的行为特性,

虚函数作为运行过程中多态的基础,主要是针

对对象的,而构造函数是在对象产生之前运行

的,因此虚构造函数是没有意义的;可以声明

虚析构函数,析构函数的功能是在该类对象消

亡之前进行一些必要的清理工作,如果一个类

的析构函数是虚函数,那么,由它派生而来的

所有子类的析构函数也是虚函数。析构函数设

置为虚函数之后,在使用指针引用时可以动态

联编,实现运行时的多态,保证使用基类的指

针就能够调用适当的析构函数针对不同的对

象进行清理工作。

8-5 实现重载函数Double(x),返回值为输入

参数的两倍;参数分别为整型、长整型、浮点

型、双精度型,返回值类型与参数一样。

解: 源程序:

#include

int Double(int);

long Double(long);

float Double(float);

double Double(double);

int main()

{

int myInt = 6500;

long myLong = 65000;

float myFloat = 6.5F;

double myDouble = 6.5e20;

int doubledInt;

long doubledLong;

float doubledFloat;

double doubledDouble;

cout << "myInt: " << myInt << "n";

cout << "myLong: " << myLong << "n";

cout << "myFloat: " << myFloat << "n";

cout << "myDouble: " << myDouble << "n";

doubledInt = Double(myInt);

doubledLong = Double(myLong);

doubledFloat = Double(myFloat);

doubledDouble = Double(myDouble);

cout << "doubledInt: " << doubledInt << "n";

cout << "doubledLong: " << doubledLong <<

"n";

cout << "doubledFloat: " << doubledFloat <<

"n";

cout << "doubledDouble: " << doubledDouble

<< "n";

return 0;

}

int Double(int original)

{

cout << "In Double(int)n";

return 2 * original;

}

long Double(long original)

{

cout << "In Double(long)n";

return 2 * original;

}

float Double(float original)

{

cout << "In Double(float)n";

return 2 * original;

}

double Double(double original)

{

cout << "In Double(double)n";

return 2 * original;

}

程序运行输出:

myInt: 6500

myLong: 65000

myFloat: 6.5

myDouble: 6.5e+20

In Double(int)

In Double(long)

In Double(float)

In Double(double)

DoubledInt: 13000

DoubledLong: 130000

DoubledFloat: 13

DoubledDouble: 1.3e+21

8-6 定义一个Rectangle类,有长itsWidth、宽

itsLength等属性,重载其构造函数Rectangle()

和Rectangle(int width, int length)。

解: 源程序:

#include

class Rectangle

{

public:

Rectangle();

Rectangle(int width, int length);

~Rectangle() {}

int GetWidth() const { return itsWidth; }

int GetLength() const { return itsLength; }

private:

int itsWidth;

int itsLength;

};

Rectangle::Rectangle()

{

itsWidth = 5;

itsLength = 10;

}

Rectangle::Rectangle (int width, int length)

{

itsWidth = width;

itsLength = length;

}

int main()

{

Rectangle Rect1;

cout << "Rect1 width: " << th()

<< endl;

cout << "Rect1 length: " << gth()

<< endl;

int aWidth, aLength;

cout << "Enter a width: ";

cin >> aWidth;

cout << "nEnter a length: ";

cin >> aLength;

Rectangle Rect2(aWidth, aLength);

cout << "nRect2 width: " << th()

<< endl;

cout << "Rect2 length: " << gth()

<< endl;

return 0;

}

程序运行输出:

Rect1 width: 5

Rect1 length: 10

Enter a width: 20

Enter a length: 50

Rect2 width: 20

Rect2 length: 50

8-7 定义计数器Counter类,对其重载运算符

+ 。

解: 源程序:

typedef unsigned short USHORT;

#include

class Counter

{

public:

Counter();

Counter(USHORT initialValue);

~Counter(){}

USHORT GetItsVal()const { return itsVal; }

void SetItsVal(USHORT x) {itsVal = x; }

Counter operator+ (const Counter &);

private:

USHORT itsVal;

};

Counter::Counter(USHORT initialValue):

itsVal(initialValue)

{

}

Counter::Counter():

itsVal(0)

{

}

Counter Counter::operator+ (const Counter &

rhs)

{

return Counter(itsVal + Val());

}

int main()

{

Counter varOne(2), varTwo(4), varThree;

varThree = varOne + varTwo;

cout << "varOne: " << Val()<<

endl;

cout << "varTwo: " << Val() <<

endl;

cout << "varThree: " << Val()

<< endl;

return 0;

}

程序运行输出:

varOne: 2

varTwo: 4

varThree: 6

8-8 定义一个哺乳动物Mammal类,再由此派

生出狗Dog类,二者都定义 Speak()成员函

数,基类中定义为虚函数,定义一个Dog类

的对象,调用Speak函数,观察运行结果。

解: 源程序:

#include

class Mammal

{

public:

Mammal():itsAge(1) { cout << "Mammal

"; }

~Mammal() { cout << "Mammal

"; }

virtual void Speak() const { cout << "Mammal

speak!n"; }

};

class Dog : public Mammal

{

public:

Dog() { cout << ""; }

~Dog() { cout << ""; }

void Speak()const { cout << "Woof!n"; }

};

int main()

{

Mammal *pDog = new Dog;

pDog->Speak();

return 0;

}

程序运行输出:

Woof!

8-9 定义一个Shape抽象类,在此基础上派生

出Rectangle和Circle,二者都有GetArea()函

数计算对象的面积,GetPerim()函数计算对象

的周长。

解: 源程序:

#include

class Shape

{

public:

Shape(){}

~Shape(){}

virtual float GetArea() =0 ;

virtual float GetPerim () =0 ;

};

class Circle : public Shape

{

public:

Circle(float radius):itsRadius(radius){}

~Circle(){}

float GetArea() { return 3.14 * itsRadius *

itsRadius; }

float GetPerim () { return 6.28 * itsRadius; }

private:

float itsRadius;

};

class Rectangle : public Shape

{

public:

Rectangle(float len, float width):

itsLength(len), itsWidth(width){};

~Rectangle(){};

virtual float GetArea() { return itsLength *

itsWidth; }

float GetPerim () { return 2 * itsLength + 2 *

itsWidth; }

virtual float GetLength() { return itsLength; }

virtual float GetWidth() { return itsWidth; }

private:

float itsWidth;

float itsLength;

};

void main()

{

Shape * sp;

sp = new Circle(5);

cout << "The area of the Circle is " <<

sp->GetArea () << endl;

cout << "The perimeter of the Circle is " <<

sp->GetPerim () << endl;

delete sp;

sp = new Rectangle(4,6);

cout << "The area of the Rectangle is " <<

sp->GetArea() << endl;

cout << "The perimeter of the Rectangle is " <<

sp->GetPerim () << endl;

delete sp;

}

程序运行输出:

The area of the Circle is 78.5

The perimeter of the Circle is 31.4

The area of the Rectangle is 24

The perimeter of the Rectangle is 20

8-10 对Point类重载++(自增)、--(自减)

运算符

解: #include

class Point

{

public:

Point& operator++();

Point operator++(int);

Point& operator--();

Point operator--(int);

Point() { _x = _y = 0; }

int x() { return _x; }

int y() { return _y; }

private:

int _x, _y;

};

Point& Point::operator++()

{

_x++;

_y++;

return *this;

}

Point Point::operator++(int)

{

Point temp = *this;

++*this;

return temp;

}

Point& Point::operator--()

{

_x--;

_y--;

return *this;

}

Point Point::operator--(int)

{

Point temp = *this;

--*this;

return temp;

}

void main()

{

Point A;

cout << "A的值为:" << A.x() << " , " << A.y()

<< endl;

A++;

cout << "A的值为:" << A.x() << " , " << A.y()

<< endl;

++A;

cout << "A的值为:" << A.x() << " , " << A.y()

<< endl;

A--;

cout << "A的值为:" << A.x() << " , " << A.y()

<< endl;

--A;

cout << "A的值为:" << A.x() << " , " << A.y()

<< endl;

}

程序运行输出:

A的值为:0 , 0

A的值为:1 , 1

A的值为:2 , 2

A的值为:1 , 1

A的值为:0 , 0

8-11 定义一个基类BaseClass,从它派生出类

DerivedClass,BaseClass有成员函数fn1()、

fn2(),fn1()是虚函数,DerivedClass也有成员

函数fn1()、fn2(),在主程序中定义一个

DerivedClass的对象,分别用BaseClass和

DerivedClass的指针来调用fn1()、fn2(),观察

运行结果。

解: #include

class BaseClass

{

public:

virtual void fn1();

void fn2();

};

void BaseClass::fn1()

{

cout << "调用基类的虚函数fn1()" << endl;

}

void BaseClass::fn2()

{

cout << "调用基类的非虚函数fn2()" << endl;

}

class DerivedClass : public BaseClass

{

public:

void fn1();

void fn2();

};

void DerivedClass::fn1()

{

cout << "调用派生类的函数fn1()" << endl;

}

void DerivedClass::fn2()

{

cout << "调用派生类的函数fn2()" << endl;

}

void main()

{

DerivedClass aDerivedClass;

DerivedClass *pDerivedClass =

&aDerivedClass;

BaseClass *pBaseClass = &aDerivedClass;

pBaseClass->fn1();

pBaseClass->fn2();

pDerivedClass->fn1();

pDerivedClass->fn2();

}

程序运行输出:

调用派生类的函数fn1()

调用基类的非虚函数fn2()

调用派生类的函数fn1()

调用派生类的函数fn2()

8-12 定义一个基类BaseClass,从它派生出

类DerivedClass,BaseClass中定义虚析构函

数,在主程序中将一个DerivedClass的对象地

址赋给一个BaseClass的指针,观察运行过程。

解: #include

class BaseClass {

public:

virtual ~BaseClass() {

cout << "~BaseClass()" << endl;

}

};

class DerivedClass : public BaseClass {

public:

~DerivedClass() {

cout << "~DerivedClass()" << endl;

}

};

void main()

{

BaseClass* bp = new DerivedClass;

delete bp; } 程序运行输出:

~DerivedClass()

~BaseClass()

8-13 定义Point类,有成员变量X、Y,为其

定义友元函数实现重载+。

解: #include

class Point

{

public:

Point() { X = Y = 0; }

Point( unsigned x, unsigned y ) { X = x; Y = y; }

unsigned x() { return X; }

unsigned y() { return Y; }

void Print() { cout << "Point(" << X << ", " <<

Y << ")" << endl; }

friend Point operator+( Point& pt, int nOffset );

friend Point operator+( int nOffset, Point& pt );

private:

unsigned X;

unsigned Y;

};

Point operator+( Point& pt, int nOffset )

{

Point ptTemp = pt;

ptTemp.X += nOffset;

ptTemp.Y += nOffset;

return ptTemp;

}

Point operator+( int nOffset, Point& pt )

{

Point ptTemp = pt;

ptTemp.X += nOffset;

ptTemp.Y += nOffset;

return ptTemp;

}

void main()

{

Point pt( 10, 10 );

();

pt = pt + 5; // Point + int

();

pt = 10 + pt; // int + Point

();

}

程序运行输出:

Point(10, 10)

Point(15, 15)

Point(25, 25)

8-14 为某公司设计一个人事管理系统,其基

本功能为输入、编辑、查看和保存公司的人事

档案。职工人事档案包括姓名、性别、出生日

期、婚姻状况、所在部门、职务和工资。

程 序:

由于列表框尚未初始化,所以为CEmpDlg类

重载OnInitDialog()成员函数(可使用

ClassWizard完成),并添加相应代码:

BOOL CEmpDlg::OnInitDialog() {

CListBox *pLB = (CListBox

*)GetDlgItem(IDC_DEPT);

pLB->InsertString(-1, "办公室");

pLB->InsertString(-1, "开发部");

pLB->InsertString(-1, "生产部");

pLB->InsertString(-1, "销售部");

pLB->InsertString(-1, "人事部");

return CDialog::OnInitDialog();

}

其中GetDlgItem()为对话框类的成员函数,

用于取对话框控件的指针。

为项目添加有关自定义的职工类CEmployee。

选择Developer Studio菜单的Insert/New

Class…选项,调出New Class对话框。在Class

Type组合框中选择Generic(普通类),填写

类名CEmployee,在对话框下方的Base class

(es)框中输入基类CObject。

在Workspace窗口的Class View中选择生成的

CEmployee类的定义,添加代码:

class CEmployee : public CObject {

DECLARE_SERIAL(CEmployee)

public:

CString m_strName; // 姓名

int m_nSex; // 性别

COleDateTime m_tBirthdate; // 出生日期

BOOL m_bMarried; // 婚否

CString m_strDept; // 工作部门

CString m_strPosition; // 职务

float m_fSalary; // 工资

CEmployee(){}

CEmployee& operator = (CEmployee& e);

virtual ~CEmployee();

virtual void Serialize(CArchive &ar);

};

CEmployee类的对象即为一个职工的档案,我

们用序列化实现文档的存取,所以要为

CEmployee类编写序列化代码。这包括

DECLARE_SERIAL()宏和

IMPLEMENT_SERIAL()宏(在CEmployee

类的源代码文件中),一个没有参数的构造函

数,重载的赋值运算符和Serialize()成员函

数。在CEmployee类的源代码文件中添加以

下代码:

IMPLEMENT_SERIAL(CEmployee, CObject,

1)

// 重载的赋值运算符

CEmployee& CEmployee::operator =

(CEmployee& e)

{

m_strName = e.m_strName;

m_nSex = e.m_nSex;

m_tBirthdate = e.m_tBirthdate;

m_bMarried = e.m_bMarried;

m_strDept = e.m_strDept;

m_strPosition = e.m_strPosition;

m_fSalary = e.m_fSalary;

return *this;

}

// 序列化函数

void CEmployee::Serialize(CArchive& ar)

{

CObject::Serialize(ar);

if(ing())

{

ar << m_strName;

ar << m_nSex;

ar << m_tBirthdate;

ar << m_bMarried;

ar << m_strDept;

ar << m_strPosition;

ar << m_fSalary;

}

else

{

ar >> m_strName;

ar >> m_nSex;

ar >> m_tBirthdate;

ar >> m_bMarried;

ar >> m_strDept;

ar >> m_strPosition;

ar >> m_fSalary;

}

}

然后修改文档类CMyDocument类定义,添加

一个CEmployee类的数组:

#include "employee.h"

#define MAX_EMPLOYEE 1000

class CMy1501Doc : public CDocument

{

DECLARE_DYNCREATE(CMy1501Doc)

public:

CEmployee m_empList[MAX_EMPLOYEE];

int m_nCount;

public:

virtual BOOL OnNewDocument();

virtual void Serialize(CArchive& ar);

virtual void DeleteContents();

DECLARE_MESSAGE_MAP()

};

为了节省篇幅,这段程序经过删节,与原来由

AppWizard生成的程序有所不同。其中黑体部

分为要添加的代码。注意重载成员函数

DeleteContents()可以手工进行,也可以通

过ClassWizard进行。Serialize()和

DeleteContents()两个成员函数的代码如下:

void CMy1501Doc::Serialize(CArchive& ar)

{

if(ing())

ar << m_nCount;

else

ar >> m_nCount;

for(int i=0; i

m_empList[i].Serialize(ar);

}

void CMy1501Doc::DeleteContents() {

m_nCount = 0; // 在打开文件和建立新文件时

将数组大小置0

CDocument::DeleteContents();

}

即在文档类的Serialize()函数中,数据的序

列化工作是通过调用Cemployee类的Serialize

()函数实现的。

实际上,要为本程序添加的大部分代码均在视

图类中。首先在视图类CmyView类的定义中

添加一个用于记录当前操作的是哪个记录的

数据成员:

int m_nCurrEmp;

并为视图类重载OnInitialUpdate()成员函数,

在其中初始化该变量:

void CMy1501View::OnInitialUpdate() {

CView::OnInitialUpdate();

m_nCurrEmp = 0;

Invalidate();

}

视图类的OnDraw()成员函数用于显示正在

操作的职工档案:

void CMy1501View::OnDraw(CDC* pDC)

{

CMy1501Doc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

// 显示职工人数和当前职工编号

CString s;

("职工人数: %d", pDoc->m_nCount);

pDC->SetTextColor(RGB(255, 0, 0));

pDC->TextOut( 40, 40, s);

("职工编号: %d", m_nCurrEmp+1);

pDC->TextOut(340, 40, s);

pDC->MoveTo( 40, 70);

pDC->LineTo(600, 70);

// 如果档案非空, 显示当前记录

if(pDoc->m_nCount > 0)

{

// 显示栏目名称

pDC->SetTextColor(RGB(0, 0, 0));

pDC->TextOut(140, 90, "姓 名:");

pDC->TextOut(140, 130, "性 别:");

pDC->TextOut(140, 170, "出生日期: ");

pDC->TextOut(140, 210, "婚姻状态:");

pDC->TextOut(140, 250, "部 门:");

pDC->TextOut(140, 290, "职 务:");

pDC->TextOut(140, 330, "工 资:");

// 显示栏目内容

pDC->SetTextColor(RGB(0, 0, 255));

pDC->TextOut(300, 90,

pDoc->m_empList[m_nCurrEmp].m_strName);

if(pDoc->m_empList[m_nCurrEmp].m_nSex==

0)

pDC->TextOut(300, 130, "男");

else

pDC->TextOut(300, 130, "女");

s =

pDoc->m_empList[m_nCurrEmp].m_tBirthdate

.Format("%Y.%m.%d");

pDC->TextOut(300, 170, s);

if(pDoc->m_empList[m_nCurrEmp].m_bMarrie

d)

pDC->TextOut(300, 210, "已婚");

else

pDC->TextOut(300, 210, "未婚");

pDC->TextOut(300, 250,

pDoc->m_empList[m_nCurrEmp].m_strDept);

pDC->TextOut(300, 290,

pDoc->m_empList[m_nCurrEmp].m_strPositio

n);

("%8.2f",

pDoc->m_empList[m_nCurrEmp].m_fSalary);

pDC->TextOut(300, 330, s);

}

}

在编辑资源时,我们框架窗口添加了5个菜单

选项,并将对应的消息响应函数映射到了视图

类中。这些消息响应函数的代码如下:

void CMy1501View::OnAppend() {

CMy1501Doc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

CEmpDlg dlg;

if(l() == IDOK)

{

pDoc->m_nCount++;

m_nCurrEmp = pDoc->m_nCount-1;

pDoc->m_empList[m_nCurrEmp].m_strName =

dlg.m_strName;

pDoc->m_empList[m_nCurrEmp].m_nSex =

dlg.m_nSex;

pDoc->m_empList[m_nCurrEmp].m_tBirthdate

= dlg.m_tBirthdate;

pDoc->m_empList[m_nCurrEmp].m_bMarried

= dlg.m_bMarried;

pDoc->m_empList[m_nCurrEmp].m_strDept =

dlg.m_strDept;

pDoc->m_empList[m_nCurrEmp].m_strPositio

n = dlg.m_strPosition;

pDoc->m_empList[m_nCurrEmp].m_fSalary =

dlg.m_fSalary;

pDoc->SetModifiedFlag();

Invalidate();

}

}

// 删除当前记录

void CMy1501View::OnDelete() {

CMy1501Doc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

if(pDoc->m_nCount)

{

for(int i=m_nCurrEmp; im_nCount-1;

i++)

pDoc->m_empList[i] =

pDoc->m_empList[i+1];

pDoc->m_nCount--;

if(m_nCurrEmp > pDoc->m_nCount-1)

m_nCurrEmp = pDoc->m_nCount-1;

pDoc->SetModifiedFlag();

Invalidate();

}

}

// 编辑当前记录

void CMy1501View::OnEdit() {

CMy1501Doc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

if(pDoc->m_nCount)

{

CEmpDlg dlg;

dlg.m_strName =

pDoc->m_empList[m_nCurrEmp].m_strName;

dlg.m_nSex =

pDoc->m_empList[m_nCurrEmp].m_nSex;

dlg.m_tBirthdate =

pDoc->m_empList[m_nCurrEmp].m_tBirthdate

;

dlg.m_bMarried =

pDoc->m_empList[m_nCurrEmp].m_bMarried;

dlg.m_strDept =

pDoc->m_empList[m_nCurrEmp].m_strDept;

dlg.m_strPosition =

pDoc->m_empList[m_nCurrEmp].m_strPositio

n;

dlg.m_fSalary =

pDoc->m_empList[m_nCurrEmp].m_fSalary;

if(l() == IDOK)

{

pDoc->m_empList[m_nCurrEmp].m_strName =

dlg.m_strName;

pDoc->m_empList[m_nCurrEmp].m_nSex =

dlg.m_nSex;

pDoc->m_empList[m_nCurrEmp].m_tBirthdate

= dlg.m_tBirthdate;

pDoc->m_empList[m_nCurrEmp].m_bMarried

= dlg.m_bMarried;

pDoc->m_empList[m_nCurrEmp].m_strDept =

dlg.m_strDept;

pDoc->m_empList[m_nCurrEmp].m_strPositio

n = dlg.m_strPosition;

pDoc->m_empList[m_nCurrEmp].m_fSalary =

dlg.m_fSalary;

pDoc->SetModifiedFlag();

Invalidate();

}

}

}

// 查看后一记录

void CMy1501View::OnNext() {

CMy1501Doc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

if(pDoc->m_nCount > 1)

{

if(m_nCurrEmp == pDoc->m_nCount-1)

m_nCurrEmp = 0;

else

m_nCurrEmp++;

}

Invalidate();

}

// 查看前一记录

void CMy1501View::OnPrev() {

CMy1501Doc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

if(pDoc->m_nCount > 1)

{

if(m_nCurrEmp == 0)

m_nCurrEmp = pDoc->m_nCount-1;

else

m_nCurrEmp--;

}

Invalidate();

}

8-15 用基于对话框的应用程序结构实现例

14-5的彩色吹泡泡程序。由于对话框本身结

构简单,没有明显的客户区,颜色也不醒目,

所以我们在对话框上自行建立一个矩形区域

作为吹泡泡的客户区,并通过一个“颜色设置”

按钮来设置泡泡的颜色。

说 明:用AppWizard建立一个基于对话框的

应用程序框架(参看15.4:“用AppWizard生

成基于对话框的应用程序”),所有设置均使用

缺省值。

使用对话框模板编辑器编辑作为主界面窗口

的对话框模板,将其上的静态文本控件和

“Cancel”按钮删除,将“OK”按钮的Caption

设置为“完成”,并将对话框大小调整为400

×300左右。

为对话框模板添加一个Picture控件,将其

Type设置为Frame,Color设置为Black,并

设置Sunken属性(在Styles选项卡中)。调整

其位置为(7,7),大小为287×287。这个框

中即为自定义的吹泡泡客户区,所有的吹泡泡

活动均在该区域中进行。

为对话框模板添加一个按钮,将其ID改为

IDC_COLOR,Caption改为“颜色设置”。

使用ClassWizard为对话框类添加一个鼠标左

键消息响应函数OnLButtonDown()和一个

按钮命令消息响应函数OnColor()。

程 序:

在对话框类的头文件前面添加一行:

#define MAX_BUBBLE 250

并在对话框类定义中添加存放泡泡的几何参

数和颜色的数组数据成员:

CRect m_rectBubble[MAX_BUBBLE];

COLORREF m_colorBubble[MAX_BUBBLE];

int m_nBubbleCount;

以及一个存放自定义客户区矩形的数据成员

和一个存放当前泡泡颜色设置的数据成员:

CRect m_rectClient;

COLORREF m_colorCurrent;

修改对话框类的OnInitDialog()成员函数,

添加计算自定义客户区位置和大小的代码,并

将泡泡的数目初始化为0:

BOOL CMyDlg::OnInitDialog()

{

CDialog::OnInitDialog();

CStatic *pST = (CStatic

*)GetDlgItem(IDC_CLIENT);

pST->GetWindowRect(&m_rectClient);

ScreenToClient(&m_rectClient);

m_nBubbleCount = 0; return TRUE;

}

修改OnPaint()成员函数,添加画出泡泡的

有关代码:

void CMyDlg::OnPaint() {

CPaintDC dc(this);

CRgn rgn;

RectRgnIndirect(&m_rectClient); //

生成一个区域对象

ClipRgn(&rgn); // 选择区域

gle(m_rectClient); // 将客户区背景

设置

CBrush brushNew, *pbrushOld; // 白色

for(int i=0; i

{

SolidBrush(m_colorBubble[i]);

pbrushOld = Object(&brushNew);

e(m_rectBubble[i]);

Object(pbrushOld);

Object();

}

}

修改由ClassWizard生成的鼠标左键消息响应

函数OnLButtonDown(),添加吹泡泡的有关

代码:

void CMyDlg::OnLButtonDown(UINT nFlags,

CPoint point) {

if(m_nBubbleCount < MAX_BUBBLE)

{

int r = rand()%50+10;

CRect rect(point.x-r, point.y-r, point.x+r,

point.y+r);

m_rectBubble[m_nBubbleCount] = rect;

m_colorBubble[m_nBubbleCount] =

m_colorCurrent;

m_nBubbleCount++;

InvalidateRect(rect, FALSE);

}

}

最后修改由ClassWizard生成的按钮消息响应

函数OnColor(),添加调用颜色设置公用对

话框的代码:

void CMyDlg::OnColor() {

m_colorCurrent = RGB(200, 200, 200);

CColorDialog dlg(m_colorCurrent);

if(l() == IDOK)

m_colorCurrent = or();

}

[例15-3] 动画播放器程序,可用文件查找公

用对话框打开AVI文件并播放。

程 序:首先在对话框类定义中添加一个存放

AVI文件名的变量:

CString m_strAviname;

并在OnInitDialog()函数中添加初始化代码:

m_strAviname = _T(“”);

修改对应于3个按钮的消息响应函数:

void CMy1503Dlg::OnOpen() {

CFileDialog dlg(TRUE, ".AVI", "*.AVI",

OFN_FILEMUSTEXIST|OFN_LONGNAMES|

OFN_PATHMUSTEXIST,

"*.AVI", this);

if(l() == IDOK)

{

m_();

m_strAviname = hName();

m_(LPCTSTR(m_strAviname));

}

}

void CMy1503Dlg::OnPlay() {

m_(0, 0xffff, 0xffff);

}

void CMy1503Dlg::OnStop() {

m_();

}

8-16 编写一个计算器程序。该计算器使用编

辑控件直接输入数据,并有“加”、“减”、“乘”、

“除”、“平方根”和“倒数”计算功能,如图

15-4。

程 序:

在对话框类中添加以下数据成员:

int m_nOP; // 运算符

double m_fResult; // 运算中间结果

并在OnInitDialog()函数中添加相应的初始

化代码:

m_fResult = 0.0;

m_nOP = 0;

为对话框类添加一个Calc()成员函数:

void CMy1504Dlg::Calc() // 计算

{

UpdateData(TRUE);

switch(m_nOP)

{

case 0: // 第1运算对象

m_fResult = m_fInput;

break;

case 1: // +

m_fResult += m_fInput;

break;

case 2: // -

m_fResult -= m_fInput;

break;

case 3: // *

m_fResult *= m_fInput;

break;

case 4: // /

m_fResult -= m_fInput;

break;

case 5: // 1/X

m_fResult = 1/m_fInput;

break;

case 6: // sqrt(X)

m_fResult = sqrt(m_fInput);

break;

}

m_fInput = m_fResult;

UpdateData(FALSE);

}

最后为所有按钮的消息响应函数添加代码:

void CMy1504Dlg::OnAdd() // 加法

{

Calc();

m_nOP = 1;

}

void CMy1504Dlg::OnSub() // 减法

{

Calc();

m_nOP = 2;

}

void CMy1504Dlg::OnMul() // 乘法

{

Calc();

m_nOP = 3;

}

void CMy1504Dlg::OnDiv() // 除法

{

Calc();

m_nOP = 4;

}

void CMy1504Dlg::OnReciprocal() // 倒数

{

m_nOP = 5;

Calc();

m_nOP = 0;

}

void CMy1504Dlg::OnSqrt() // 平方根

{

m_nOP = 6;

Calc();

m_nOP = 0;

}

void CMy1504Dlg::OnSetfocusInput() // 为输

入数据做准备

{

m_fInput = 0.0;

UpdateData(FALSE); }

void CMy1504Dlg::OnClear() // 清除

{

m_fResult = 0.0;

m_fInput = 0.0;

m_nOP = 0;

UpdateData(FALSE);

}

void CMy1504Dlg::OnCalc() // 显示计算结果

{

Calc();

m_nOP = 0;

}

第 九 章 群体类

9-1 程序中提示输入班级中的学生人数N,再

依次提示用户输入N个人在课程A的考试成

绩,然后计算出平均成绩,显示出来。请使用

教材第9章中的数组类模板Array定义浮点型

数组储存考试成绩值。

解: //array.h即为教材中的例程9-2,参见教

材第9章

//array.h

#ifndef ARRAY_CLASS

#define ARRAY_CLASS #include

#include

#ifndef NULL

const int NULL = 0;

#endif // NULL

//错误类型集合,共有三种类型的错误:数组

大小错误、内存分配错误和下标越界

enum ErrorType {invalidArraySize,

memoryAllocationError, indexOutOfRange};

//错误信息

char *errorMsg[] =

{

"Invalid array size", "Memory allocation error",

"Invalid index: "

};

//数组类模板

template class Array

{

private:

T* alist; //T类型指针,用于存放动态分配的数

组内存首地址

int size; //数组大小(元素个数)

// 错误处理函数

void Error(ErrorType error,int badIndex=0)

const;

public:

Array(int sz = 50); //构造函数

Array(const Array& A); //拷贝构造函数

~Array(void); //析构函数

//重载"="使数组对象可以整体赋值

Array& operator= (const Array& rhs);

//重载"[]"与T*,使Array对象可以起到C++

普通数组的作用

T& operator[](int i); operator T* (void) const;

int ListSize(void) const; // 取数组的大小

void Resize(int sz); // 修改数组的大小

};

//以下为类成员函数的实现

//模扳函数Error实现输出错误信息的功能

template

void Array::Error(ErrorType error, int

badIndex) const

{

cout << errorMsg[error]; //根据错误类型,输出

相应的错误信息

// for indexOutOfRange, print the bad index

if (error == indexOutOfRange)

cout << badIndex; //如果是下标越界错,输出

错误的下标

cout << endl;

exit(1);

}

// 构造函数

template

Array::Array(int sz)

{

if (sz <= 0) //sz为数组大小(元素个数),若小

于0,则输出错误信息

Error(invalidArraySize);

size = sz; // 将元素个数赋值给变量size

alist = new T[size]; //动态分配size个T类型的

元素空间

if (alist == NULL) //如果分配内存不成功,输

出错误信息

Error(memoryAllocationError);

}

// 析构函数

template

Array::~Array(void)

{ delete [] alist;

}

// 拷贝构造函数

template

Array::Array(const Array& X)

{

//从对象X取得数组大小,并赋值给当前对象

的成员

int n = ; size = n;

//为对象申请内存并进行出错检查

alist = new T[n]; // 动态分配n个T类型的元

素空间

if (alist == NULL) //如果分配内存不成功,输

出错误信息

Error(memoryAllocationError);

// 从对象X复制数组元素到本对象 T* srcptr

= ; // 是对象X的数组首地址

T* destptr = alist; // alist是本对象中的数组首

地址

while (n--) // 逐个复制数组元素

*destptr++ = *srcptr++;

}

// 重载"="运算符,将对象rhs赋值给本对象。

实现对象之间的整体赋值

template

Array& Array::operator= (const

Array& rhs)

{

int n = ; // 取rhs的数组大小

//如果本对象中数组大小与rhs不同,则删除

数组原有内存,然后重新分配

if (size != n)

{

delete [] alist; // 删除数组原有内存

alist = new T[n]; // 重新分配n个元素的内存

if (alist == NULL) //如果分配内存不成功,输

出错误信息

Error(memoryAllocationError);

size = n; //记录本对象的数组大小

}

// 从rhs向本对象复制元素

T* destptr = alist;

T* srcptr = ;

while (n--) *destptr++ = *srcptr++;

// 返回指向本对象的指针

return *this;

}

// 重载下标操作符,实现与普通数组一样通

过下标访问元素,并且具有越界检查功能

template

T& Array::operator[] (int n)

{

// 检查下标是否越界

if (n < 0 || n > size-1)

Error(indexOutOfRange,n);

// 返回下标为n的数组元素

return alist[n];

}

//重载指针转换操作符,使指向T类对象的指

针成为当前对象中私有数组的首地址。

//因而可以象使用普通数组首地址一样使用T

类型指针

template

Array::operator T* (void) const

{

// 返回当前对象中私有数组的首地址

return alist;

}

//取当前数组的大小

template

int Array::ListSize(void) const

{

return size;

}

// 将数组大小修改为sz

template

void Array::Resize(int sz)

{

// 检查是否sz<= 0

if (sz <= 0) Error(invalidArraySize);

// 如果指定的大小与原有大小一样,什么也

不做

if (sz == size)

return;

// 申请新的数组内存,并测试是否申请成功

T* newlist = new T[sz];

if (newlist == NULL)

Error(memoryAllocationError);

// 将sz与size中较小的一个赋值给n

int n = (sz <= size) ? sz : size;

// 将原有数组中前n个元素复制到新数组中

T* srcptr = alist; // 原数组alist的首地址

T* destptr = newlist; // 新数组newlist的首地

while (n--) // 复制数组元素

*destptr++ = *srcptr++;

// 删除原数组

delete[] alist;

// 使alist 指向新数组,并更新size

alist = newlist;

size = sz;

}

#endif // ARRAY_CLASS

//test9_

#include

#include

void main()

{

int n;

double AverScore,TotalScore = 0;

cout << "请输入学生人数:";

cin >> n;

Array Score(n);

for (int i=0; i

{

cout << "请输入第" << i+1 <<"个学生的课程

A成绩(0~100):";

cin >> Score[i];

TotalScore += Score[i];

}

AverScore = TotalScore/n;

cout << "平均成绩为" << setprecision(4) <<

AverScore << endl;

}

程序运行输出:

请输入学生人数:3

请输入第1个学生的课程A成绩(0~100):80

请输入第2个学生的课程A成绩(0~100):80

请输入第3个学生的课程A成绩(0~100):81

平均成绩为80.33

9-2 链表中的一个节点包含哪些数据成员?

单链表和双向链表的区别是什么?

解: 链表是由系列结点组成的,每一个结点

包括数据域和指向链表中其它结点的指针(即

下一个结点的地址)。每个结点中只有一个指

向后继结点指针的链表称为单链表。如果链表

中每个结点中有两个用于连接其它结点的指

针,一个指向前趋结点(称前趋指针),另一

个指向后继结点(称后继指针),这样的链表

称为双向链表。

9-3 链表中元素的最大数目为多少?

解:

链表中元素的最大数目没有固定限制,只取决

于可用的内存数量。

9-4 在双向链表中使用的节点类与单链表中

使用的节点类相比,应有何不同?试定义并实

现双向链表中使用的节点类DNODE。

解: 每一个结点包括数据域和指向链表中其

它结点的指针(即下一个结点的地址),单链

表中使用的节点类中每个结点中只有一个指

向后继结点指针;在双向链表中使用的节点类

中有两个用于连接其它结点的指针,一个指向

前趋结点(称前趋指针),另一个指向后继结

点(称后继指针)。双向链表中使用的节点类

可如下定义:

//dnode.h

#ifndef DOUBLY_LINKED_NODE_CLASS

#define DOUBLY_LINKED_NODE_CLASS

template

class DNode

{

private:

// circular links to the left and right

DNode *left;

DNode *right;

public: // data is public T data;

// constructors

DNode(void); DNode (const T& item);

// list modification methods

void InsertRight(DNode *p);

void InsertLeft(DNode *p);

DNode *DeleteNode(void);

// obtain address of the next node to the left or

right

DNode *NextNodeRight(void) const;

DNode *NextNodeLeft(void) const;

};

// constructor that creates an empty list and

// leaves the data uninitialized. use for header

template

DNode::DNode(void)

{

// initialize the node so it points to itself

left = right = this;

}

// constructor that creates an empty list and

initializes data

template

DNode::DNode(const T& item)

{

// set node to point to itself and initialize data

left = right = this;

data = item;

}

// insert a node p to the right of current node

template

void DNode::InsertRight(DNode *p)

{

// link p to its successor on the right

p->right = right;

right->left = p;

// link p to the current node on its left

p->left = this;

right = p;

}

// insert a node p to the left of current node

template

void DNode::InsertLeft(DNode *p)

{

// link p to its successor on the left

p->left = left;

left->right = p;

// link p to the current node on its right

p->right = this;

left = p;

}

// unlink the current node from the list and

return its address

template

DNode *DNode::DeleteNode(void)

{

// node to the left must be linked to current

node's right

left->right = right;

// node to the right must be linked to current

node's left

right->left = left;

// return the address of the current node

return this;

}

// return pointer to the next node on the right

template

DNode *DNode::NextNodeRight(void)

const

{

return right;

}

// return pointer to the next node on the left

template

DNode *DNode::NextNodeLeft(void)

const

{

return left;

}

#endif // DOUBLY_LINKED_NODE_CLASS

9-5 使用本章中的链表类定义两个整型链表

A和B,分别插入5个元素,然后把B中的

元素加入A的尾部。

解: 本题的解答见“实验指导” 部分“实验

九”。

9-6 从教材第9章的链表类LinkedList中派生

有序链表类OrderList,添加成员函数

InsertOrder,实现链表元素的有序(递增)插

入。定义两个整型有序链表A和B,分别按

递增顺序插入5个元素,然后把B中的元素

插入A中,保持A中递增顺序不变。

解: #include

#include "link.h" //参见"实验指导" 部分"实验

九"

template

class Link:public LinkedList

{

public:

void InsertOrder(const T& item);

};

template

void Link::InsertOrder(const T& item)

{

Reset();

while (!EndOfList())

{

if (item < Data())

break;

Next();

} InsertAt(item);

}

void main(void)

{

Link A,B;

int i, item;

cout << "请输入加入链表A的五个整数:";

for ( i=0; i<5; i++ )

{

cin >> item;

Order(item);

}

cout << "请输入加入链表B的五个整数:";

for ( i=0; i<5; i++ )

{

cin >> item;

Order(item);

}

cout << endl << "有序链表A中的元素为:";

();

while(!ist())

{

cout << () << " ";

(); }

cout << endl << "有序链表B中的元素为:";

();

while(!ist())

{

Order(());

cout << () << " ";

(); }

cout << endl << "加入链表B中元素后,链表

A中的元素为:";

();

while(!ist())

{

cout << () << " ";

(); }

}

程序运行输出:

请输入加入链表A的五个整数:1 3 7 6 5

请输入加入链表B的五个整数:2 6 8 5 4

链表A中的元素为:1 3 5 6 7 链表B中的元

素为:2 4 5 6 8 加入链表B中元素后,链表A

中的元素为:1 2 3 4 5 5 6 6 7 8 9-7 什么叫作

栈?对栈中元素的操作有何特性?

解: 栈是只能从一端访问的线性群体,可以

访问的这一端称栈顶,另一端称栈底。对栈顶

位置的标记称为栈顶指针,对栈底位置的标记

称为栈底指针。向栈顶添加元素称为“压入

栈”,删除栈顶元素称为“弹出栈”。栈中元素

的添加和删除操作具有“后进先出”(LIFO)

的特性。

9-8 在标准C++类库中,栈类(stack)的成员

函数stack::push()在栈顶端添加元素,

stack::pop()从非空栈的栈顶端中删除一个元

素,stack::empty()判断栈是否为空, stack::top()

返回非空栈的栈顶元素,stack::size()返回栈中

元素的个数,请构造一个整型栈,然后对这个

栈调用以上几个函数,体会栈这种数据结构的

特点和其成员函数的用法。

解: #include

#include

using namespace std ;

typedef stack STACK_INT;

void main()

{

STACK_INT stack1;

cout << "() returned " <<

(()? "true": "false") << endl; //

Function 3

cout << "(2)" << endl;

(2);

if (!()) // Function 3

cout << "() returned " <<

() << endl; // Function 1

cout << "(5)" << endl;

(5);

if (!()) // Function 3

cout << "() returned " <<

() << endl; // Function 1

cout << "(11)" << endl;

(11);

if (!()) // Function 3

cout << "() returned " <<

() << endl; // Function 1

// Modify the top item. Set it to 6.

if (!()) { // Function 3

cout << "()=6;" << endl;

()=6; // Function 1

}

// Repeat until stack is empty

while (!()) { // Function 3

const int& t=(); // Function 2

cout << "() equals " << ()

<< endl;

cout << "() returned " << t << endl;

cout << "()" << endl;

();

}

cout << "() equals " << ()

<< endl;

}

程序运行输出:

() returned true

(2)

() returned 2

(5)

() returned 5

(11)

() returned 11

()=6;

() returned 6

()

() returned 5

()

() returned 2

()

9-9 在标准C++类库中,对栈类(stack)重载

了= =、! =、>、> =、<、< =等运算符,以对

两个不同的栈进行算术比较运算操作,请构造

一个整型栈,以 = =、< 运算为例,对两个栈

进行算术比较运算,体会其比较归者规则;运

行程序,观察其输出。

解: 源程序:

#include

#include

using namespace std ;

typedef stack STACK_DOUBLE;

void main()

{

STACK_DOUBLE stack1,stack2;

// Add item 4.0 to Stack1. Stack1 contains 4.0.

cout << "(4.0) s1=[4.0]" << endl;

(4.0);

// Add item 3.0 to Stack1. Stack1 contains

3.0(top) and 4.0(bottom).

cout << "(3.0) s1=[3.0 4.0]" << endl;

(3.0);

// Add item 4.0 to Stack2. Stack2 contains 4.0

(top=bottom).

cout << "(4.0) s2=[4.0]" << endl;

(4.0);

// Compare if Stack1 is smaller than Stack2.

Should return False.

cout << "stack1==stack2 is " <<

((stack1==stack2)? "True": "False") << endl;

cout << "stack1

((stack1

endl;

// Add item 6.0 to Stack2. Stack2 contains

6.0(top) and 4.0(bottom).

cout << "(6.0) s2=[6.0 4.0]" << endl;

(6.0);

// Compare if Stack1 is smaller than Stack2.

Should return True.

cout << "stack1==stack2 is " <<

((stack1==stack2)? "True": "False") << endl;

cout << "stack1

((stack1

endl;

// Add item 8.0 to Stack2. Stack2 contains

8.0(top), 6.0 and

// 4.0(bottom).

cout << "(8.0) s2=[8.0 6.0 4.0]" <<

endl;

(8.0);

// Compare if Stack1 is smaller than Stack2.

Should return True.

cout << "stack1==stack2 is " <<

((stack1==stack2)? "True": "False") << endl;

cout << "stack1

((stack1

endl;

// Delete item 8.0 from Stack2.

cout << "() s2=[6.0 4.0]" << endl;

();

// Delete item 6.0 from Stack2.

cout << "() s2=[4.0]" << endl;

();

// Add item 3.0 to Stack2. Stack2 contains

3.0(top) and 4.0(bottom).

cout << "(3.0) s2=[3.0 4.0]" << endl;

(3.0);

// Compare if Stack1 is smaller than Stack2.

Should return False.

cout << "stack1==stack2 is " <<

((stack1==stack2)? "True": "False") << endl;

cout << "stack1

((stack1

endl;

// Delete item 3.0 from Stack2.

cout << "() s2=[4.0]" << endl;

();

// Delete item 4.0 from Stack2.

cout << "() s2=[]" << endl;

();

// Add item 8.0 to Stack2. Stack2 contains

8.0(top=bottom).

cout << "(8.0) s2=[8.0]" << endl;

(8.0);

// Compare if Stack1 is smaller than Stack2.

Should return True.

cout << "stack1==stack2 is " <<

((stack1==stack2)? "True": "False") << endl;

cout << "stack1

((stack1

endl;

}

程序运行输出: (4.0) s1=[4.0]

(3.0) s1=[3.0 4.0]

(4.0) s2=[4.0]

stack1

(6.0) s2=[6.0 4.0]

stack1

(8.0) s2=[8.0 6.0 4.0]

stack1

() s2=[6.0 4.0]

() s2=[4.0]

(3.0) s2=[3.0 4.0]

stack1

() s2=[4.0]

() s2=[]

(8.0) s2=[8.0]

stack1

9-10 什么叫作队列?对队列中元素的操作有

和特性?

解: 队列是只能向一端添加元素,从另一端

删除元素的线性群体,可以添加元素的一端称

队尾,可以删除元素的一端称队头。对队头位

置的标记称为队头指针,对队尾位置的标记称

为队尾指针。向队尾添加元素称为“入队”,

删除队头元素称为“出队”。队列中元素的添

加和删除操作具有“先进先出”(FIFO)的特

性。

9-11 在标准C++类库中,队列类(queue)的

成员函数queue::push()在队列一端添加元素,

queue::pop()从非空的队列中删除最后一个元

素,queue::empty()判断队列是否为空,

queue::back()返回非空队列的最后一个元素,

queue::front()返回非空队列的第一个元素,

queue::size()返回队列中元素的个数,请构造

一个整型队列和一个字符型队列,然后对这两

个队列调用以上几个函数,体会队列这种数据

结构的特点和其成员函数的用法。

解: #include

#include

using namespace std ;

typedef queue INTQUEUE;

typedef queue CHARQUEUE;

void main(void)

{

int size_q;

INTQUEUE q;

CHARQUEUE p;

// Insert items in the queue(uses list)

(42);

(100);

(49);

(201);

// Output the size of queue

size_q = ();

cout << "size of q is:" << size_q << endl;

// Output items in queue using front()

// and use pop() to get to next item until

// queue is empty

while (!())

{

cout << () << endl;

();

}

// Insert items in the queue(uses deque)

("cat");

("ape");

("dog");

("mouse");

("horse");

// Output the item inserted last using back()

cout << () << endl;

// Output the size of queue

size_q = ();

cout << "size of p is:" << size_q << endl;

// Output items in queue using front()

// and use pop() to get to next item until

// queue is empty

while (!())

{

cout << () << endl;

();

}

}

程序运行输出:

size of q is:4

42

100

49

201

horse

size of p is:5

cat

ape

dog

mouse

horse

9-12 实际应用中,双向队列比普通队列更加

常用。在标准C++类库中,双向队列类(deque)

的成员函数queue::assign ()给一个双向队列重

新赋值,queue::swap()交换两个双向队列中的

元素,queue::begin()返回指向双向队列中的第

一个元素的指针,queue:: end()返回指向双向

队列中的最后一个元素的指针,请构造一个整

型双向队列,然后对这个队列调用以上几个函

数,体会队列这种数据结构的特点和其成员函

数的用法。

解: #include

#include

using namespace std;

typedef deque CHARDEQUE;

void print_contents (CHARDEQUE deque,

char*);

void main()

{

//create a with 3 A's

CHARDEQUE a(3,'A');

//create b with 4 B's.

CHARDEQUE b(4,'B');

//print out the contents

print_contents (a,"a");

print_contents (b,"b");

//swap a and b

(b);

print_contents (a,"a");

print_contents (b,"b");

// let us swap it back

(b);

print_contents (a,"a");

print_contents (b,"b");

//assign the contents of b to a

((),());

print_contents (a,"a");

//assign the first two items of b to a

((),()+2);

print_contents (a,"a");

//assign 3 'Z's to a

(3,'Z');

print_contents (a,"a");

}

//function to print the contents of deque

void print_contents (CHARDEQUE deque, char

*name)

{

CHARDEQUE::iterator pdeque;

cout <<"The contents of "<< name <<" : ";

for(pdeque = ();

pdeque != ();

pdeque++)

{

cout << *pdeque <<" " ;

}

cout<

}

程序运行输出:

The contents of a : A A A

The contents of b : B B B B

The contents of a : B B B B

The contents of b : A A A

The contents of a : A A A

The contents of b : B B B B

The contents of a : B B B B

The contents of a : B B

The contents of a : Z Z Z

9-13 在标准C++类库中,双向队列类(deque)

的成员函数queue::front()返回一个非空双向

队列的第一个元素,queue:: back()返回一个非

空双向队列的最后一个元素,请构造一个字符

型双向队列,体会这几个成员函数的用法。

解: #include

#include

using namespace std;

typedef deque CHARDEQUE;

void print_contents (CHARDEQUE deque,

char*);

void main()

{

//create a with A, B, C and D

CHARDEQUE a;

_back('A');

_back('B');

_back('C');

_back('D');

//print out the contents

print_contents (a,"a");

cout <<"The first element of a is " <<()

<

cout <<"The last element of a is " <<()

<

//now let us modify the first and last elements

//using reference ,front() and back()

CHARDEQUE::reference reffront=();

CHARDEQUE::reference refback=();

reffront='X';

refback='Y';

//print out the contents

print_contents (a,"a");

}

//function to print the contents of deque

void print_contents (CHARDEQUE deque, char

*name)

{

CHARDEQUE::iterator pdeque;

cout <<"The contents of "<< name <<" : ";

for(pdeque = ();

pdeque != ();

pdeque++)

{

cout << *pdeque <<" " ;

}

cout<

}

程序运行输出:

The contents of a : A B C D

The first element of a is A

The last element of a is D

The contents of a : X B C Y

9-14 在标准C++类库中,双向队列类(deque)

的成员函数queue::insert()往一个双向队列中

插入元素,queue::push_front(const T& x)往一

个双向队列的头端插入一个元素,

queue::pop_front()从一个双向队列的头端删

除一个元素,queue::push_back(const T& x)往

一个双向队列的尾端插入一个元素,

queue::pop_back(const T& x)从一个双向队列

的尾端删除一个元素,请构造一个字符型双向

队列,体会这几个成员函数的用法。

解: #include

#include

using namespace std;

typedef deque CHARDEQUE;

void print_contents (CHARDEQUE deque);

void main()

{

//create a with 3 A's

CHARDEQUE a(3,'A');

//create b with 2 B's.

CHARDEQUE b(2,'B');

//print out the contents

print_contents (a);

print_contents (b);

//insert 'X' to the beginning of a

((),'X');

print_contents (a);

//insert 'Y' to the end of a

((),'Y');

print_contents (a);

//inset 3 'Z's to one item before the end of a

(()-1,3,'Z');

print_contents (a);

//insert to the end of a from b

((),(),());

print_contents (a);

}

//function to print the contents of deque

void print_contents (CHARDEQUE deque)

{

CHARDEQUE::iterator pdeque;

cout <<"The output is: ";

for(pdeque = ();

pdeque != ();

pdeque++)

{

cout << *pdeque <<" " ;

}

cout<

}

程序运行输出:

The output is: A A A

The output is: B B

The output is: X A A A

The output is: X A A A Y

The output is: X A A A Z Z Z Y

The output is: X A A A Z Z Z Y B B

9-15 在标准C++类库中,双向队列类(deque)

的长度是可变的,成员函数resize( n, T x = T())

可加长已有的双向队列对象,size() const返回

队列长度,max_size() const返回系统可支持

的最大双向队列长度,请构造一个字符型双向

队列,体会这几个成员函数的用法。

解: #include

#include

using namespace std;

typedef deque CHARDEQUE;

void print_contents (CHARDEQUE deque,

char*);

void main()

{

//create a with A, B, C and D

CHARDEQUE a;

_back('A');

_back('B');

_back('C');

_back('D');

//print out the contents

print_contents (a,"a");

cout <<"max_size of a is " <<_size()

<

cout <<"size of a is " <<() <

//let us increase the size to 10

// and set the new elements to be 'X'

(10,'X');

print_contents (a,"a");

cout <<"size of a is " <<() <

//let us resize it to 5

(5);

print_contents (a,"a");

cout <<"size of a is " <<() <

cout <<"max_size of a is still " <<_size()

<

}

//function to print the contents of deque

void print_contents (CHARDEQUE deque, char

*name)

{

CHARDEQUE::iterator pdeque;

cout <<"The contents of "<< name <<" : ";

for(pdeque = ();

pdeque != ();

pdeque++)

{

cout << *pdeque <<" " ;

}

cout<

}

程序运行输出:

The contents of a : A B C D

max_size of a is 4294967295

size of a is 4

The contents of a : A B C D X X X X X X

size of a is 10

The contents of a : A B C D X

size of a is 5

max_size of a is still 4294967295

9-16 深度为n的二叉树的最大节点数是多

少?有以下节点数的二叉树的最小深度为多

少?1) 5; 2) 9; 3) 25; 4) 250。

解: 深度为n的二叉树的最大节点数为

2n+1-1;

以上二叉树的最小深度为:1)2; 2)3; 3) 4;

4) 7

9-17 请建立如图所示的一个简单的二叉树,

结点数据类型为字符型。

解: //nodetree.h 即为教材中例程9-11,参见

教材第9章

#ifndef TREENODE_CLASS #define

TREENODE_CLASS

#ifndef NULL

const int NULL = 0;

#endif // NULL

template

class TreeNode

{

private:

TreeNode *left; //左指针

TreeNode *right; //右指针

public:

T data; //结点数据

// 构造函数

TreeNode (const T& item, TreeNode *lptr =

NULL,

TreeNode *rptr = NULL);

// 访问指针域的函数

TreeNode* Left(void) const; //取得左指针

TreeNode* Right(void) const; //取得右指

// 删除当前结点的左右子树

void release();

};

// 构造函数,初始化数据域和指针域

template

TreeNode::TreeNode (const T& item,

TreeNode *lptr,

TreeNode *rptr): data(item), left(lptr),

right(rptr)

{}

//取得左指针

template

TreeNode* TreeNode::Left(void) const

{

return left;

}

//取得右指针

template

TreeNode* TreeNode::Right(void)

const

{

return right;

}

// 删除当前结点的左右子树

template

void TreeNode::release()

{

if(left)

{ //删除左子树

left->release();

delete left;

left=NULL;

}

if(right)

{ //删除右子树

right->release();

delete right;

right=NULL;

}

}

#endif // TREENODE_CLASS

//test9_

#include

#include "treenode.h"

void main(void)

{

TreeNode D('D'),E('E');

TreeNode B('B',&D,&E),C('C');

TreeNode A('A',&B,&C);

}

第 十 章 群体数据的组织

10-1 简单说明插入排序的算法思想。

解: 插入排序的基本思想是:每一步将一个

待排序元素按其关键字值的大小插入到已排

序序列的适当位置上,直到待排序元素插入完

为止。

10-2 初始化整型数组

data1[]={1,3,5,7,9,11,13,15,17,19,2,4,6,8,10,12,

14,16,18,20},调用教材中的直接插入排序函

数模板进行排序,对此函数模板稍做修改,加

入输出语句,在每插入一个待排序元素后显示

整个数组,观察排序过程中数据的变化,加深

对插入排序算法的理解。

解: #include template

void InsertionSort(T A[], int n)

{

int i, j;

T temp;

// 将下标为1~n-1的元素逐个插入到已排序

序列中适当的位置

for (i = 1; i < n; i++) {

//从A[i-1]开始向A[0]方向扫描各元素,寻找适

当位置插入A[i]

j = i;

temp = A[i];

while (j > 0 && temp < A[j-1]) { //逐个比较,

直到temp>=A[j-1]时,j便是应插入的位置。

//若达到j==0,则0是应插入的位置。

A[j] = A[j-1];

//将元素逐个后移,以便找到插入位置时可立

即插入。

j--;

}

// 插入位置已找到,立即插入。

A[j] = temp;

//输出数据

for(int k=0;k

cout << A[k] << " ";

cout << endl;

//结束输出 }

}

void main()

{

int i;

int

data1[]={1,3,5,7,9,11,13,15,17,19,2,4,6,8,10,12,

14,16,18,20};

cout << "排序前的数据:" << endl;

for(i=0;i<20;i++)

cout << data1[i] << " ";

cout << endl;

cout << "开始排序..." << endl;

InsertionSort(data1, 20);

cout << "排序后的数据:" << endl;

for(i=0;i<20;i++)

cout << data1[i] << " ";

cout << endl;

}

程序运行输出:

排序前的数据:

1 3 5 7 9 11 13 15 17 19 2 4 6 8 10 12 14 16 18

20 开始排序...

1 3 5 7 9 11 13 15 17 19 2 4 6 8 10 12 14 16 18

20 1 3 5 7 9 11 13 15 17 19 2 4 6 8 10 12 14 16

18 20 1 3 5 7 9 11 13 15 17 19 2 4 6 8 10 12 14

16 18 20 1 3 5 7 9 11 13 15 17 19 2 4 6 8 10 12

14 16 18 20 1 3 5 7 9 11 13 15 17 19 2 4 6 8 10

12 14 16 18 20 1 3 5 7 9 11 13 15 17 19 2 4 6 8

10 12 14 16 18 20 1 3 5 7 9 11 13 15 17 19 2 4 6

8 10 12 14 16 18 20 1 3 5 7 9 11 13 15 17 19 2 4

6 8 10 12 14 16 18 20 1 3 5 7 9 11 13 15 17 19 2

4 6 8 10 12 14 16 18 20 1 2 3 5 7 9 11 13 15 17

19 4 6 8 10 12 14 16 18 20 1 2 3 4 5 7 9 11 13

15 17 19 6 8 10 12 14 16 18 20 1 2 3 4 5 6 7 9

11 13 15 17 19 8 10 12 14 16 18 20 1 2 3 4 5 6 7

8 9 11 13 15 17 19 10 12 14 16 18 20 1 2 3 4 5 6

7 8 9 10 11 13 15 17 19 12 14 16 18 20 1 2 3 4 5

6 7 8 9 10 11 12 13 15 17 19 14 16 18 20 1 2 3 4

5 6 7 8 9 10 11 12 13 14 15 17 19 16 18 20 1 2 3

4 5 6 7 8 9 10 11 12 13 14 15 16 17 19 18 20 1 2

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

排序后的数据:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

20 10-3 简单说明选择排序的算法思想。

解: 选择类排序的基本思想是:每次从待排

序序列中选择一个关键字最小的元素,(当需

要按关键字升序排列时),顺序排在已排序序

列的最后,直至全部排完。

10-4 初始化整型数组

data1[]={1,3,5,7,9,11,13,15,17,19,2,4,6,8,10,12,

14,16,18,20},调用教材中的直接选择排序函

数模板进行排序,对此函数模板稍做修改,加

入输出语句,每对一个待排序元素排序后显示

整个数组,观察排序过程中数据的变化,加深

对直接选择排序算法的理解。

解: #include

// 辅助函数:交换x和y的值

template

void Swap (T &x, T &y)

{

T temp;

temp = x;

x = y;

y = temp;

}

// 用选择法对数组A的n个元素进行排序

template

void SelectionSort(T A[], int n)

{

int smallIndex; //每以趟中选出的最小元素之

下标

int i, j;

// sort A[0]..A[n-2], and A[n-1] is in place

for (i = 0; i < n-1; i++) {

smallIndex = i; //最小元素之下标初值设为i

// 在元素A[i+1]..A[n-1]中逐个比较显出最小

for (j = i+1; j < n; j++) // smallIndex始终记录

当前找到的最小值的下标

if (A[j] < A[smallIndex])

smallIndex = j;

// 将这一趟找到的最小元素与A[i]交换

Swap(A[i], A[smallIndex]);

//输出数据

for(int k=0;k

cout << A[k] << " ";

cout << endl;

//结束输出 }

}

void main()

{

int i;

int

data1[]={1,3,5,7,9,11,13,15,17,19,2,4,6,8,10,12,

14,16,18,20};

cout << "排序前的数据:" << endl;

for(i=0;i<20;i++)

cout << data1[i] << " ";

cout << endl;

cout << "开始排序..." << endl;

SelectionSort(data1, 20);

cout << "排序后的数据:" << endl;

for(i=0;i<20;i++)

cout << data1[i] << " ";

cout << endl;

}

程序运行输出:

排序前的数据:

1 3 5 7 9 11 13 15 17 19 2 4 6 8 10 12 14 16 18

20 1 3 5 7 9 11 13 15 17 19 2 4 6 8 10 12 14 16

18 20 1 2 5 7 9 11 13 15 17 19 3 4 6 8 10 12 14

16 18 20 1 2 3 7 9 11 13 15 17 19 5 4 6 8 10 12

14 16 18 20 1 2 3 4 9 11 13 15 17 19 5 7 6 8 10

12 14 16 18 20 1 2 3 4 5 11 13 15 17 19 9 7 6 8

10 12 14 16 18 20 1 2 3 4 5 6 13 15 17 19 9 7 11

8 10 12 14 16 18 20 1 2 3 4 5 6 7 15 17 19 9 13

11 8 10 12 14 16 18 20 1 2 3 4 5 6 7 8 17 19 9

13 11 15 10 12 14 16 18 20 1 2 3 4 5 6 7 8 9 19

17 13 11 15 10 12 14 16 18 20 1 2 3 4 5 6 7 8 9

10 17 13 11 15 19 12 14 16 18 20 1 2 3 4 5 6 7 8

9 10 11 13 17 15 19 12 14 16 18 20 1 2 3 4 5 6 7

8 9 10 11 12 17 15 19 13 14 16 18 20 1 2 3 4 5 6

7 8 9 10 11 12 13 15 19 17 14 16 18 20 1 2 3 4 5

6 7 8 9 10 11 12 13 14 19 17 15 16 18 20 1 2 3 4

5 6 7 8 9 10 11 12 13 14 15 17 19 16 18 20 1 2 3

4 5 6 7 8 9 10 11 12 13 14 15 16 19 17 18 20 1 2

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 19 18 20 1

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

20 排序后的数据:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

20 10-5 简单说明交换排序的算法思想。

解: 交换排序的基本思想是:两两比较待排

序序列中的元素,并交换不满足顺序要求的各

对元素,直到全部满足顺序要求为止。

10-6 初始化整型数组

data1[]={1,3,5,7,9,11,13,15,17,19,2,4,6,8,10,12,

14,16,18,20},调用教材中的起泡排序函数模

板进行排序,对此函数模板稍做修改,加入输

出语句,每完成一趟起泡排序后显示整个数

组,观察排序过程中数据的变化,加深对起泡

排序算法的理解。

解: #include

// 辅助函数:交换x和y的值

template

void Swap (T &x, T &y)

{

T temp;

temp = x;

x = y;

y = temp;

}

// 用起泡法对数组A的n个元素进行排序

template

void BubbleSort(T A[], int n)

{

int i,j; int lastExchangeIndex; //用于记录每趟

被交换的最后一对元素中较小的下标

i = n-1; // i是下一趟需参与排序交换的元素之

最大下标

while (i > 0)

//持续排序过程,直到最后一趟排序没有交换

发生,或已达n-1趟

{

lastExchangeIndex = 0; //每一趟开始时,设置

交换标志为0(未交换)

for (j = 0; j < i; j++) //每一趟对元素A[0]..A[i]

进行比较和交换

if (A[j+1] < A[j]) //如果元素A[j+1] < A[j],交

换之

{ Swap(A[j],A[j+1]);

lastExchangeIndex = j; //记录被交换的一对元

素中较小的下标

}

// 将i设置为本趟被交换的最后一对元素中

较小的下标

i = lastExchangeIndex;

//输出数据

for(int k=0;k

cout << A[k] << " ";

cout << endl;

//结束输出 }

}

void main()

{

int i;

int

data1[]={1,3,5,7,9,11,13,15,17,19,2,4,6,8,10,12,

14,16,18,20};

cout << "排序前的数据:" << endl;

for(i=0;i<20;i++)

cout << data1[i] << " ";

cout << endl;

cout << "开始排序..." << endl;

BubbleSort(data1, 20);

cout << "排序后的数据:" << endl;

for(i=0;i<20;i++)

cout << data1[i] << " ";

cout << endl;

}

程序运行输出:

排序前的数据:

1 3 5 7 9 11 13 15 17 19 2 4 6 8 10 12 14 16 18

20 开始排序...

1 3 5 7 9 11 13 15 17 2 4 6 8 10 12 14 16 18 19

20 1 3 5 7 9 11 13 15 2 4 6 8 10 12 14 16 17 18

19 20 1 3 5 7 9 11 13 2 4 6 8 10 12 14 15 16 17

18 19 20 1 3 5 7 9 11 2 4 6 8 10 12 13 14 15 16

17 18 19 20 1 3 5 7 9 2 4 6 8 10 11 12 13 14 15

16 17 18 19 20 1 3 5 7 2 4 6 8 9 10 11 12 13 14

15 16 17 18 19 20 1 3 5 2 4 6 7 8 9 10 11 12 13

14 15 16 17 18 19 20 1 3 2 4 5 6 7 8 9 10 11 12

13 14 15 16 17 18 19 20 1 2 3 4 5 6 7 8 9 10 11

12 13 14 15 16 17 18 19 20 1 2 3 4 5 6 7 8 9 10

11 12 13 14 15 16 17 18 19 20 排序后的数据:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

20 10-7 教材中的排序算法都是升序排序,稍

做修改后即可完成降序排序,以起泡排序法为

例,完成降序的起泡排序函数模板,初始化整

型数组

data1[]={1,3,5,7,9,11,13,15,17,19,2,4,6,8,10,12,

14,16,18,20},调用降序起泡排序函数模板进

行排序,加入输出语句,每完成一趟起泡排序

后显示整个数组,观察排序过程中数据的变

化。

解: #include

// 辅助函数:交换x和y的值

template

void Swap (T &x, T &y)

{

T temp;

temp = x;

x = y;

y = temp;

}

// 用起泡法对数组A的n个元素进行排序

template

void BubbleSort(T A[], int n)

{

int i,j; int lastExchangeIndex; //用于记录每趟

被交换的最后一对元素中较小的下标

i = n-1; // i是下一趟需参与排序交换的元素之

最大下标

while (i > 0) //持续排序过程,直到最后一趟排

序没有交换发生,或已达n-1趟

{

lastExchangeIndex = 0; //每一趟开始时,设置

交换标志为0(未交换)

for (j = 0; j < i; j++) //每一趟对元素A[0]..A[i]

进行比较和交换

if (A[j+1] > A[j]) //如果元素A[j+1] < A[j],交

换之

{ Swap(A[j],A[j+1]);

lastExchangeIndex = j; //记录被交换的一对元

素中较小的下标

}

// 将i设置为本趟被交换的最后一对元素中

较小的下标

i = lastExchangeIndex;

//输出数据

for(int k=0;k

cout << A[k] << " ";

cout << endl;

//结束输出 }

}

void main()

{

int i;

int

data1[]={1,3,5,7,9,11,13,15,17,19,2,4,6,8,10,12,

14,16,18,20};

cout << "排序前的数据:" << endl;

for(i=0;i<20;i++)

cout << data1[i] << " ";

cout << endl;

cout << "开始排序..." << endl;

BubbleSort(data1, 20);

cout << "排序后的数据:" << endl;

for(i=0;i<20;i++)

cout << data1[i] << " ";

cout << endl;

}

程序运行输出:

排序前的数据:

1 3 5 7 9 11 13 15 17 19 2 4 6 8 10 12 14 16 18

20 开始排序...

3 5 7 9 11 13 15 17 19 2 4 6 8 10 12 14 16 18 20

1 5 7 9 11 13 15 17 19 3 4 6 8 10 12 14 16 18 20

2 1 7 9 11 13 15 17 19 5 4 6 8 10 12 14 16 18 20

3 2 1 9 11 13 15 17 19 7 5 6 8 10 12 14 16 18 20

4 3 2 1 11 13 15 17 19 9 7 6 8 10 12 14 16 18 20

5 4 3 2 1 13 15 17 19 11 9 7 8 10 12 14 16 18 20

6 5 4 3 2 1 15 17 19 13 11 9 8 10 12 14 16 18 20

7 6 5 4 3 2 1 17 19 15 13 11 9 10 12 14 16 18 20

8 7 6 5 4 3 2 1 19 17 15 13 11 10 12 14 16 18 20

9 8 7 6 5 4 3 2 1 19 17 15 13 11 12 14 16 18 20

10 9 8 7 6 5 4 3 2 1 19 17 15 13 12 14 16 18 20

11 10 9 8 7 6 5 4 3 2 1 19 17 15 13 14 16 18 20

12 11 10 9 8 7 6 5 4 3 2 1 19 17 15 14 16 18 20

13 12 11 10 9 8 7 6 5 4 3 2 1 19 17 15 16 18 20

14 13 12 11 10 9 8 7 6 5 4 3 2 1 19 17 16 18 20

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 19 17 18 20

16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 19 18 20

17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 19 20

18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 20

19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

排序后的数据:

20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2

1 10-8 简单说明顺序查找的算法思想。

解: 顺序查找是一种最简单、最基本的查找

方法。

其基本思想是:从数组的首元素开始,逐个元

素与待查找的关键字进行比较,直到找到相等

的。若整个数组中没有与待查找关键字相等的

元素,就是查找不成功。

10-9 初始化整型数组

data1[]={1,3,5,7,9,11,13,15,17,19,2,4,6,8,10,12,

14,16,18,20},提示用户输入一个数字,调用

教材中的顺序查找函数模板找出它的位置。

解: #include

template

int SeqSearch(T list[], int n, T key)

{

for(int i=0;i < n;i++)

if (list[i] == key)

return i; return -1; }

void main()

{

int i, n;

int

data1[]={1,3,5,7,9,11,13,15,17,19,2,4,6,8,10,12,

14,16,18,20};

cout << "输入想查找的数字(1~20):";

cin >> n;

cout << "数据为:" << endl;

for(i=0;i<20;i++)

cout << data1[i] << " ";

cout << endl;

i = SeqSearch ( data1 , 20 , n );

if (i<0)

cout << "没有找到数字" << n << endl;

else

cout << n << "是第" << i+1 << "个数字" <<

endl;

}

程序运行输出:

输入想查找的数字(1~20):6

数据为:

1 3 5 7 9 11 13 15 17 19 2 4 6 8 10 12 14 16 18

20 6是第13个数字

10-10 简单说明折半查找的算法思想。

解: 如果是在一个元素排列有序的数组中进

行查找,可以采用折半查找方法。

折半查找方法的基本思想是:对于已按关键字

排序的序列,经过一次比较,可将序列分割成

两部分,然后只在有可能包含待查元素的一部

分中继续查找,并根据试探结果继续分割,逐

步缩小查找范围,直至找到或找不到为止。

10-11 初始化整型数组

data1[]={1,3,5,7,9,11,13,15,17,19,2,4,6,8,10,12,

14,16,18,20},提示用户输入一个数字,调用

教材中的折半查找函数模板找出它的位置。

解: #include

// 用折半查找方法,在元素呈升序排列的数

组list中查找值为key的元素

template

int BinSearch(T list[], int n, T key)

{

int mid, low, high;

T midvalue;

low=0;

high=n-1;

while (low <= high) // low <= high表示整个数

组尚未查找完

{

mid = (low+high)/2; // 求中间元素的下标

midvalue = list[mid]; // 取出中间元素的值

if (key == midvalue) return mid; // 若找到,返

回下标

else if (key < midvalue)

high = mid-1; // 若key < midvalue将查找范围

缩小到数组的前一半

else

low = mid+1; // 否则将查找范围缩小到数组

的后一半

}

return -1; // 没有找到返回-1

}

void main()

{

int i, n;

int

data1[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,

17,18,19,20};

cout << "输入想查找的数字(1~20):";

cin >> n;

cout << "数据为:" << endl;

for(i=0;i<20;i++)

cout << data1[i] << " ";

cout << endl;

i = BinSearch ( data1 , 20 , n );

if (i<0)

cout << "没有找到数字" << n << endl;

else

cout << n << "是第" << i+1 << "个数字" <<

endl;

}

程序运行输出:

输入想查找的数字(1~20):9

数据为:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

20 9

第十一流类库与输入

是第9个数字

/输出

11-1 什么叫做流?流的提取和插入是指什

么?I/O流在C++中起着怎样的作用?

解: 流是一种抽象,它负责在数据的生产者

和数据的消费者之间建立联系,并管理数据的

流动,一般意义下的读操作在流数据抽象中被

称为(从流中)提取,写操作被称为(向流中)

插入。操作系统是将键盘、屏幕、打印机和通

信端口作为扩充文件来处理的,I/O流类就是

用来与这些扩充文件进行交互,实现数据的输

入与输出。

11-2 cerr和clog有何区别?

解: cerr 标准错误输出,没有缓冲,发送给

它的内容立即被输出,适用于立即向屏幕输出

的错误信息;clog 类似于cerr,但是有缓冲,

缓冲区满时被输出,在向磁盘输出时效率更

高。

11-3 使用I/O流以文本方式建立一个文件

,写入字符“已成功写入文件!”,用

其它字处理程序(例如windows的记事本程

序Notepad)打开,看看是否正确写入。

解: #include void main() {

ofstream file1("");

file1 << "已成功写入文件!";

();

}

程序运行后的内容为:已成功写入文

件!

11-4 使用I/O流以文本方式打开上一题建立

的文件,读出其内容显示出来,看看

是否正确。

解: #include

void main() {

char ch;

ifstream file2("");

while ((ch))

cout << ch;

();

}

程序运行输出:

已成功写入文件!

11-5 使用I/O流以文本方式打开上题建立的

文件,在次此文件后面添加字符“已

成功添加字符!”,然后读出整个文件的内容显

示出来,看看是否正确。

解: #include

void main() {

ofstream file1("",ios::app);

file1 << "已成功添加字符!";

();

char ch;

ifstream file2("");

while ((ch))

cout << ch;

();

}

程序运行输出:

已成功写入文件!已成功添加字符!

11-6 定义一个dog类,包含体重和年龄两个

成员变量及相应的成员函数,声明一个实例

dog1,体重为5,年龄为10,使用I/O流把

dog1的状态写入磁盘文件,再声明另一个实

例dog2,通过读文件把dog1的状态赋给dog2。

分别使用文本方式和二进制方式操作文件,看

看结果有何不同;再看看磁盘文件的ASCII

码有何不同。

解: 以两种方式操作,程序运行结果一样,

但磁盘文件的ASCII码不同,使用二进制方

式时,磁盘文件的ASCII码为05 00 00 00 0A

00 00 00,使用文本方式时,磁盘文件的ASCII

码为05 00 00 00 0D 0A 00 00 00,这是因为此

时系统自动把0A转换为了0D 0A。

#include

class dog

{

public:

dog(int weight, long days):itsWeight(weight),

itsNumberDaysAlive(days){}

~dog(){}

int GetWeight()const { return itsWeight; }

void SetWeight(int weight) { itsWeight =

weight; }

long GetDaysAlive()const { return

itsNumberDaysAlive; }

void SetDaysAlive(long days)

{ itsNumberDaysAlive = days; }

private:

int itsWeight;

long itsNumberDaysAlive;

};

int main() // returns 1 on error

{

char fileName[80];

cout << "Please enter the file name: ";

cin >> fileName;

ofstream fout(fileName);

// ofstream fout(fileName,ios::binary);

if (!fout)

{

cout << "Unable to open " << fileName << " for

writing.n";

return(1);

}

dog Dog1(5,10);

((char*) &Dog1,sizeof Dog1);

();

ifstream fin(fileName);

// ifstream fin(fileName,ios::binary);

if (!fin)

{

cout << "Unable to open " << fileName << " for

reading.n";

return(1);

}

dog Dog2(2,2);

cout << "Dog2 weight: " << ght()

<< endl;

cout << "Dog2 days: " << sAlive()

<< endl;

((char*) &Dog2, sizeof Dog2);

cout << "Dog2 weight: " << ght()

<< endl;

cout << "Dog2 days: " << sAlive()

<< endl;

();

return 0;

}

程序运行输出:

Please enter the file name: a

Dog2 weight: 2

Dog2 days: 2

Dog2 weight: 5

Dog2 days: 10

11-7 观察下面的程序,说明每条语句的作用,

看看程序执行的结果。

#include

using namespace ::std;

void main()

{

ios_base::fmtflags original_flags = ();

//1 cout<< 812<<'|';

(ios_base::left,ios_base::adjustfield); //2

(10); //3

cout<< 813 << 815 << 'n';

(ios_base::adjustfield); //4

ion(2);

(ios_base::uppercase|ios_base::scientifi

c); //5

cout << 831.0 ;

(original_flags); //6

}

解: //1保存现在的格式化参数设置,以便将

来恢复这些设置;

//2 把对齐方式由缺省的右对齐改为左对齐;

//3 把输出域的宽度由缺省值0改为10;

//4 清除对齐方式的设置;

//5 更改浮点数的显示设置;

//6 恢复原来的格式化参数设置。

程序运行输出:

812|813 815

8.31E+02

11-8 提示用户输入一个十进制整数,分别用

十进制、八进制和十六进制形式输出。

解: #include

void main() {

int n;

cout << "请输入一个十进制整数:";

cin >> n;

cout << "这个数的十进制形式为:" << dec <<

n << endl;

cout << "这个数的八进制形式为:" << oct <<

n << endl;

cout << "这个数的十六进制形式为:" << hex

<< n << endl;

} 程序运行输出:

请输入一个十进制整数:15

这个数的十进制形式为:15

这个数的八进制形式为:17

这个数的十六进制形式为:f

11-9 编写程序实现如下功能:打开指定的一

个文本文件,在每一行前加行号。

解: //

#include

#include

#include

void main(int argc, char* argv[])

{

strstream textfile;

{

ifstream in(argv[1]);

textfile << ();

}

ofstream out(argv[1]); const int bsz = 100;

char buf[bsz];

int line = 0;

while(e(buf, bsz)) {

(ios::right, ios::adjustfield);

(1);

out << ++line << ". " << buf << endl;

}

}

编译后运行程序b

运行前的内容为:

aaaaaaaaaaaa

bbbbbbbbbbbb

cccccccccccc

dddddddddddd

eeeeeeeeeeee

ffffffffffff

gggggggggggg

hhhhhhhhhhhh

运行后的内容为:

1. aaaaaaaaaaaa

2. bbbbbbbbbbbb

3. cccccccccccc

4. dddddddddddd

5. eeeeeeeeeeee

6. ffffffffffff

7. gggggggggggg

8. hhhhhhhhhhhh

本文是通过网络收集,如有侵权

请告知,我会第一时间处理。


本文标签: 函数 成员 程序 对象 元素