C语言程序设计(郑莉)课后习题答案

更新时间:2023-11-18 02:22:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

C++语言程序设计(清华大学郑莉)课后习题答案

第 一 章 概述

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

迄今为止计算机程序设计语言的发展经历了机器语言、汇编语言、高级语言等阶段,C++语言是一种面向对象的编程语言,也属于高级语言。

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

面向对象的编程语言与以往各种编程语言有根本的不同,它设计的出发点就是为了能更直接的描述客观世界中存在的事物以及它们之间的关系。面向对象的编程语言将客观事物看作具有属性和行为的对象,通过抽象找出同一类对象的共同属性(静态特征)和行为(动态特征),形成类。通过类的继承与多态可以很方便地实现代码重用,大大缩短了软件开发周期,并使得软件风格统一。因此,面向对象的编程语言使程序能够比较直接地反问题域的本来面目,软件开发人员能够利用人类认识事物所采用的一般思维方法来进行软件开发。C++语言是目前应用最广的面向对象的编程语言。

1-3 什么是结构化程序设计方法?这种方法有哪些优点和缺点? 解:

结构化程序设计的思路是:自顶向下、逐步求精;其程序结构是按功能划分为若干个基本模块;各模块之间的关系尽可能简单,在功能上相对独立;每一模块内部均是由顺序、选择和循环三种基本结构组成;其模块化实现的具体方法是使用子程序。结构化程序设计由于采用了模块分解与功能抽象,自顶向下、分而治之的方法,从而有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子任务,便于开发和维护。 虽然结构化程序设计方法具有很多的优点,但它仍是一种面向过程的程序设计方法,它把数据和处理数据的过程分离为相互独立的实体。当数据结构改变时,所有相关的处理过程都要进行相应的修改,每一种相对于老问题的新方法都要带来额外的开销,程序的可重用性差。

由于图形用户界面的应用,程序运行由顺序运行演变为事件驱动,使得软件使用起来越来越方便,但开发起来却越来越困难,对这种软件的功能很难用过程来描述和实现,使用面向过程的方法来开发和维护都将非常困难。

1-4 什么是对象?什么是面向对象方法?这种方法有哪些特点? 解:

从一般意义上讲,对象是现实世界中一个实际存在的事物,它可以是有形的,也可以是无形的。对象是构成世界的一个独立单位,它具有自己的静态特征和动态特征。面向对象方法中的对象,是系统中用来描述客观事物的一个实体,它是用来构成系统的一个基本单位,由一组属性和一组行为构成。 面向对象的方法将数据及对数据的操作方法放在一起,作为一个相互依存、不可分离的整体--对象。对同类型对象抽象出其共性,形成类。类中的大多数数据,只能用本类的方法进行处理。类通过一个简单的外部接口,与外界发生关系,对象与对象之间通过消息进行通讯。这样,程序模块间的关系更为简单,程序模块的独立性、数据的安全性就有了良好的保障。通过实现继承与多态性,还可以大大提高程序的可重用性,使得软件的开发和维护都更为方便。

面向对象方法所强调的基本原则,就是直接面对客观存在的事物来进行软件开发,将人们在日常生活中习惯的思维方式和表达方式应用在软件开发中,使软件开发从过分专业化的方法、规则和技巧中回到客观世界,回到人们通常的思维。

1-5 什么叫做封装? 解:

封装是面向对象方法的一个重要原则,就是把对象的属性和服务结合成一个独立的系统单位,并尽可能隐蔽对象的内部细节。

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

面向对象的软件工程是面向对象方法在软件工程领域的全面应用,它包括面向对象的分析(OOA)、面向对象的设计(OOD)、面向对象的编程(OOP)、面向对象的测试(OOT)和面向对象的

1

软件维护(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<<\

2

cout<<\} 解:

#include //指示编译器将文件iostream.h中的代码

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

void main() //主函数名,void 表示函数没有返回值 { //函数体标志

cout<<\//输出字符串Hello!到标准输出设备(显示器)上。

cout<<\输出字符串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循环设置条件的第一个\前,用,分隔不同的赋值表达

3

式。 例如:

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 << \输出结果 */ return 0; } 解: 改正:

#include int main() { int i; int j;

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

cout << \输出结果 */ return 0; }

程序运行输出: i + j = 30

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

4

显示出来。 解: 源程序:

#include

int main() { int i;

cout << \请输入一个数字:\cin >> i;

cout << \您输入一个数字是\return 0; }

程序运行输出: 请输入一个数字:5 您输入一个数字是5

2-19 C++有哪几种数据类型?简述其值域。编程显示你使用的计算机中的各种数据类型的字节数。 解: 源程序:

#include

int main() {

cout << \\

cout << \<< \

cout << \<< \

cout << \\

cout << \<< \

cout << \<< \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; }

程序运行输出:

!\,./0123456789:;<>?@ABCDEFGHIJKLMNOP_QRSTUVWXYZ[\\]^'abcdefghijklmnopqrstuvwxyz<|>~s

2-21 运行下面的程序,观察其输出,与你的设想是否相同? #include int main() {

unsigned int x; unsigned int y = 100; unsigned int z = 50; x= y - z;

cout << \x = z - y;

cout << \return 0; } 解:

程序运行输出: Difference is: 50

Now difference is: 4294967246

注意,第二行的输出并非 -50,注意x、y、z的数据类型。

2-22 运行下面的程序,观察其输出,体会i++与++i的差别。 #include int main() {

5

int myAge = 39; // initialize two integers int yourAge = 39;

cout << \cout << \myAge++; // postfix increment ++yourAge; // prefix increment 变量有以下几种存储类型:

auto存储类型:采用堆栈方式分配内存空间,属于一时性存储,其存储空间可以被若干变量多次覆盖使用; register存储类型:存放在通用寄存器中;

extern存储类型:在所有函数和程序段中都可引用; static存储类型:在内存中是以固定地址存放的,在整个程序cout << \

cout << \cout << \cout << \

cout << \cout << \cout << \

cout << \cout << \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 变量有哪几种存储类型? 解:

运行期间都有效。

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

6

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)实现一个简单的菜单程序,运行时显示\A(dd) D(elete) S(ort) Q(uit), Select one:\提示用户输入,A表示增加,D表示删除,S表示排序,Q表示退出,输入为A、D、S时分别提示\数据已经增加、删除、排序。\输入为Q时程序结束。要求使用if ? else语句进行判断,用break、continue控制程序流程。 解:

#include

7

#include

void main() {

char choice,c; while(1) {

cout << \, Select one:\cin >> c;

choice = toupper(c); if (choice == 'A') {

cout << \数据已经增加. \continue; }

else if (choice == 'D') {

cout << \数据已经删除. \continue; }

else if (choice == 'S') {

cout << \数据已经排序. \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)实现一个简单的菜单程序,运行时显示\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 << \, Select one:\

cin >> choice;

switch(toupper(choice)) {

case 'A':

cout << \数据已经增加. \break; case 'D':

cout << \数据已经删除. \break; case 'S':

cout << \数据已经排序. \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

8

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 << \是质数.\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 << \是质数.\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 << \是质数.\} }

程序运行输出: 2是质数. 3是质数. 5是质数. 7是质数. 11是质数. 13是质数. 17是质数. 19是质数. 23是质数. 29是质数. 31是质数. 37是质数. 41是质数. 43是质数. 47是质数. 53是质数. 59是质数. 61是质数. 67是质数. 71是质数. 73是质数. 79是质数.

9

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 << \你猜的值太小了!\else if (n < m)

cout << \你猜的值太大了!\else

cout << \你猜对了!\}

}

//使用do?while语句 #include

void main() { int n = 18; int m = 0; do{

cout << \请猜这个数的值为多少?(0~~100):\cin >> m; if (n > m)

cout << \你猜的值太小了!\else if (n < m)

cout << \你猜的值太大了!\else

cout << \你猜对了!\}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,

10

可以,类的每一个对象都有自己的数据成员。

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 << \

cout << Jack.GetAge() << \cout << Jack.GetWeight() << \Jack.SetAge(7); Jack.SetWeight(20); cout << \

cout << Jack.GetAge() << \cout << Jack.GetWeight() << \

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;

16

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 = MyRectangle.GetArea();

cout << \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 << \长为\宽为\的矩形的面积为:\

<< r.GetArea () << endl; }

程序运行输出: 请输入矩形的长度:5 请输入矩形的宽度:4

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

4-12 定义一个\数据类型\类,能处理包含字符型、整型、浮点型三种类型的数据,给出其构造函数。

17

解:

#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 << \字符型: \break; case integer:

cout << \整型: \break;

case floating_point:

cout << \浮点型: \break; } }

void main() {

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

A.print(); B.print(); C.print(); }

程序运行输出: 字符型: 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 << \的圆的面积为:\<< p.GetArea () << endl; }

程序运行输出: 请输入圆的半径:5

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

4-14 定义一个tree类,有成员ages,成员函数grow(int years)对ages加上years,age()显示tree对象的ages的值。

18

解:

#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 << \这棵树的年龄为\}

void main() {

Tree t(12);

t.age(); t.grow(4); }

程序运行输出: 这棵树的年龄为12 这棵树的年龄为16

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

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

解:

作用域讨论的是标识符的有效范围,作用域是一个标识符在程序正文中有效的区域。C++的作用域分为函数原形作用域、块作用域(局部作用域)、类作用域和文件作用域.

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

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

可见性的一般规则是:标识符要声明在前,引用在后,在同一作用域中,不能声明同名的标识符。对于在不同的作用域声明的标识符,遵循的原则是:若有两个或多个具有包含关系的作用域,外层声明的标识符如果在内层没有声明同名标识符时仍可见,如果内层声明了同名标识符则外层标识符不可见。

5-3 下面的程序的运行结果是什么,实际运行一下,看看与你的设想有何不同。 #include void myFunction();

int x = 5, y = 7; int main() {

cout << \cout << \myFunction();

cout << \cout << \cout << \return 0; }

void myFunction() {

int y = 10;

cout << \cout << \} 解:

程序运行输出:

19

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() {

20

case 3:

brushNew.CreateSolidBrush(RGB(0, 255, 0)); break; case 4:

brushNew.CreateSolidBrush(RGB(0, 0, 255)); break; case 5:

brushNew.CreateSolidBrush(RGB(127, 127, 0)); break; case 6:

brushNew.CreateSolidBrush(RGB(127, 0, 127)); break; case 7:

brushNew.CreateSolidBrush(RGB(0, 127, 127)); break; }

penNew.CreatePen(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;

rgn.CreatePolygonRgn(m_pointList, m_nPointCount, 0); return rgn.PtInRegion(point); }

// 取拼图块的包含矩形 LPCRECT CChip::GetRect() {

static RECT rect; CRgn rgn;

rgn.CreatePolygonRgn(m_pointList, m_nPointCount, 0); rgn.GetRgnBox(&rect); rect.right++; rect.bottom++; return ▭ }

// 旋转拼图块

void CChip::Rotation() {

CRect rect; CRgn rgn;

rgn.CreatePolygonRgn(m_pointList, m_nPointCount, 0); rgn.GetRgnBox(&rect);

double x = rect.left+rect.Width()/2; // 计算旋转中心 double y = rect.top+rect.Height()/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

// 序列化

void CChip::Serialize(CArchive &ar) {

if(ar.IsStoring()) {

ar << m_nType; ar << m_nPointCount;

for(int i=0; i

ar >> m_nType; ar >> m_nPointCount;

for(int i=0; i> 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)

46

// 初始化拼图块 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

/////////////////////////////////////////////////// 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)

47

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; im_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_nCurrIndex].GetRect());

CSize offset(point-m_pointMouse);

pDoc->m_chipList[m_nCurrIndex].MoveTo(offset); InvalidateRect(pDoc->m_chipList[m_nCurrIndex].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) //

///////////////////////////////////////////////

48

#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_SHOWMAXIMIZED); 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 \// 框架窗口类

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_Bitmap.LoadBitmap(IDB_BITMAP1); BITMAP BM;

m_Bitmap.GetBitmap(&BM); m_nWidth = BM.bmWidth; m_nHeight = BM.bmHeight; }

49

// 响应绘制窗口客户区消息 void CMyWnd::OnPaint() {

CPaintDC dc(this); CDC MemDC;

MemDC.CreateCompatibleDC(NULL); MemDC.SelectObject(&m_Bitmap);

dc.BitBlt(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(\ 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 \// 框架窗口类

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_Bitmap.LoadBitmap(IDB_BITMAP1); m_Bitmap.GetBitmap(&BM); m_nWidth = BM.bmWidth; m_nHeight = BM.bmHeight; m_fTimes = 1.0; }

// 装入菜单

BOOL CMyWnd::PreCreateWindow(CREATESTRUCT &cs) { cs.hMenu

=

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(); }

// 原样显示

50

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 << \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 \ int n;

void main() { n = 20; fn1();

cout << \的值为\}

// fn1.h文件 extern int n;

void fn1() { n=30; }

程序运行输出: n的值为30

21

5-12 在函数fn1()中定义一个静态变量n,fn1()中对n的值加1,在主函数中,调用fn1()十次,显示n的值。 解:

#include

void fn1() {

static int n = 0; n++;

cout << \的值为\}

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 \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:

22

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 aCar.weight + aBoat.weight; }

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 什么叫做指针?指针中储存的地址和这个地址中的值有何区别? 解:

指针是一种数据类型,具有指针类型的变量称为指针变量。指

23

针变量存放的是另外一个对象的地址,这个地址中的值就是另一个对象的内容。

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 << \cin >> myArray[i]; }

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

cout << i << \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 << \

6-14 程序中定义一个double类型变量的指针。分别显示指针占了多少字节和指针所指的变量占了多少字节。 解:

double *counter;

cout << \cout <<

'\\nSize

of

addressed

value

==

\

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

24

解:

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 << \return 0; } 解:

指针p没有初始化,也就是没有指向某个确定的内存单元,它指向内存中的一个随机地址,给这个随机地址赋值是非常危险的。

6-18 下列程序有何问题,请改正;仔细体会使用指针时应避免出现的这个问题。 #include int Fn1(); int main() {

int a = Fn1();

cout << \return 0; }

int Fn1() {

int * p = new int (5); return *p; } 解:

此程序中给*p分配的内存没有被释放掉。 改正:

#include int* Fn1(); int main() {

int *a = Fn1();

cout << \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 {

25

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 = rhs.GetRadius(); itsRadius = new int(val); }

int SimpleCircle::GetRadius() const {

return *itsRadius; }

int main() {

SimpleCircle CircleOne, CircleTwo(9);

cout << \cout << \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 << \输入一个英语句子:\gets(text);

cout << \这个句子里有\个字母。\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++) ;

26

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 << \在\中左起第\<< \个位置。\else

cout << str2 << \不在\中。\}

程序运行输出:

输入一个英语单词: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 << \原字符串为:\reverse(str1);

cout << \倒序反转后为:\}

程序运行输出:

输入一个字符串: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 << \cin >> grades[i]; }

total = 0;

for (i = 0; i < N; i++) total += grades[i]; average = total / N;

27

cout << \}

程序运行输出: 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

MyString::MyString (const MyString & rhs) {

itsLen=rhs.GetLen();

itsMyString = new char[itsLen+1]; for (unsigned short i = 0; i

MyString::~MyString () {

delete [] itsMyString; itsLen = 0; }

MyString& MyString::operator=(const MyString & rhs) {

if (this == &rhs) return *this;

delete [] itsMyString; itsLen=rhs.GetLen();

itsMyString = new char[itsLen+1]; for (unsigned short i = 0; i

28

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 + rhs.GetLen(); MyString temp(totalLen);

for (unsigned short i = 0; i

for (unsigned short j = 0; j

void MyString::operator+=(const MyString& rhs) {

unsigned short rhsLen = rhs.GetLen(); unsigned short totalLen = itsLen + rhsLen; MyString temp(totalLen);

for (unsigned short i = 0; i

for (unsigned short j = 0; j

int main() {

MyString s1(\

cout << \

char * temp = \s1 = temp;

cout << \

char tempTwo[20];

strcpy(tempTwo,\s1 += tempTwo;

cout << \cout << \

cout << \s1[4]='x';

cout << \

cout << \

MyString s2(\MyString s3; s3 = s1+s2;

cout << \

MyString s4;

s4 = \

cout << \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

29

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 << \输入矩阵的元素\for(i=0; i<3; i++) for (j=0; j<3; j++) {

cout << \第\行第\<<\个元素为:\cin >> data[i][j]; }

cout << \输入的矩阵的为:\for(i=0; i<3; i++) {

for (j=0; j<3; j++) cout << data[i][j] << \cout << endl; }

move(data);

cout << \转置后的矩阵的为:\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

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 << \输入矩阵的元素\for(i=0; i

cout << \第\行第\<<\个元素为:\cin >> p[i*n + j]; }

30

cout << \输入的矩阵的为:\for(i=0; i

for (j=0; j

move(p, n);

cout << \转置后的矩阵的为:\for(i=0; i

for (j=0; j

程序运行输出: 请输入矩阵的维数: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 << \cout << city << \}

void main(void) {

Employee e1(\张三\平安大街3号\北京\e1.display(); cout << endl;

e1.change_name(\李四\e1.display(); cout << endl; }

程序运行输出:

张三 平安大街3号 北京 100000 李四 平安大街3号 北京 100000

31

第 七 章 继承与派生

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;

32

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 << \<< endl; delete sp;

sp = new Rectangle(4,6);

cout << \<< endl; delete sp;

sp = new Square(5);

cout << \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 << \

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 << \}

33

Dog::Dog(): itsColor (WHITE) {

cout << \}

Dog::~Dog() {

cout << \}

int main() {

Dog Jack; Jack.Speak(); Jack.WagTail();

cout << \

return 0; }

程序运行输出: Mammal constructor... Dog constructor... Mammal sound! Tail wagging... Fido is 1 years old Dog destructor... Mammal destructor...

7-7 定义一个基类,构造其派生类,在构造函数中输出提示信息,观察构造函数的执行情况。 解:

#include

class BaseClass { public: BaseClass(); };

BaseClass::BaseClass() {

cout << \构造基类对象!\ }

class DerivedClass : public BaseClass { public:

DerivedClass(); };

DerivedClass::DerivedClass() {

cout << \构造派生类对象!\}

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;

34

}

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 << \Document::PrintNameOf(); }

void main() {

Document a(\Book b(\b.PrintNameOf(); }

程序运行输出: 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; a.fn1(); }

7-10 定义object类,有weight属性及相应的操作函数,由此派生出box类,增加Height和width属性及相应的操作函数,声明一个box对象,观察构造函数与析构函数的调用顺序。 解:

#include

class object { private: int Weight; public: object() {

cout << \构造object对象\Weight = 0; }

int GetWeight(){ return Weight;} void SetWeight(int n){ Weight = n;}

~object() { cout << \析构object对象\};

class box : public object { private:

int Height,Width; public: box() {

cout << \构造box对象\Height = Width = 0; }

int GetHeight(){ return Height;} void SetHeight(int n){ Height = n;} int GetWidth(){ return Width;} void SetWidth(int n){ Width = n;}

35

~box() { cout << \析构box对象\};

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()\}

void BaseClass::fn2() {

cout << \调用基类的函数fn2()\}

class DerivedClass : public BaseClass { public: void fn1(); void fn2(); };

void DerivedClass::fn1() {

cout << \调用派生类的函数fn1()\}

void DerivedClass::fn2() {

cout << \调用派生类的函数fn2()\}

void main() {

DerivedClass aDerivedClass;

DerivedClass *pDerivedClass = &aDerivedClass; BaseClass *pBaseClass = &aDerivedClass;

aDerivedClass.fn1(); aDerivedClass.fn2(); 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

36

CAboutDlg::CAboutDlg():CDialog(CAboutDlg::IDD){} 3.在框架窗口类中添加响应鼠标右键消息的代码,包括消息响应函数说明、消息响应宏和消息响应函数定义。鼠标右键消息响应函数为:

void CMyWnd::OnRButtonDown(UINT nFlags, CPoint point) {

CAboutDlg dlg; dlg.DoModal(); }

7-13 签名留念簿程序。该程序模仿签名簿,用户使用鼠标左键点击窗口客户区后会弹出一个对话框,输入姓名后可在鼠标点击位置显示出该签名。签名的颜色、字体大小和方向随机确定。 说 明:项目建立及添加对话框模板资源的方法同例14-1。修改对话框模板的ID为IDD_NAMEDLG,Caption为“签名对话框”,并添加一个静态文本控件(Caption改为“签名”)和一个编辑控件(ID改为IDC_EDITNAME)。 程 序:

// Example 14-2:签名留念簿程序 #include #include \// 对话框类

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, rect.Size()); 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.CreateFont(m_nHeight, 0, m_nEscapement,0, 400, FALSE,FALSE,

0, OEM_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY, DEFAULT_PITCH, \楷体\

pOldFont = pDC->SelectObject(&font); switch(m_nColor) {

37

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(dlg.DoModal() == IDOK) {

int height = rand()`+12; int color = rand()%4;

int escapement = (rand()00)-600;

CString name = dlg.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

// 应用程序类

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和应用程序类与上例相同,因此下面仅列出了框架窗口类和对话框类。由于这两个类的成员函数中存在相互引用的情况,所以我们将框架窗口类的声明放在前面,接下来是对话框类的定义,并在框架窗口类之前加入了一

38

条对对话框类的声明。最后是这两个类的成员函数定义。 // 框架窗口类 #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()`+12; int color = rand()%4;

int escapement = (rand()00)-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

// 对话框类的成员函数 // 对话框类的构造函数

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); }

// 初始化对话框

39

BOOL CNameDlg::OnInitDialog() {

CDialog::OnInitDialog(); CRect rect;

GetWindowRect(&rect);

MoveWindow(0, 0, rect.Width(), rect.Height()); 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 \// 对话框类

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, rect.Size()); 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); };

40

本文来源:https://www.bwwdw.com/article/i5vv.html

Top