Uploaded by Bean Ad

C++实验手册

advertisement
C++面向对象技术课程实验
指导用书
课 程 名 称 : 程 序 设 计 ( II) 实 验
单
位:中山大学软件学院
编
写:张锋
2010 年 2 月 (第 六 版 )
一、目的与要求:
通 过 上 机 实 验 理 解 C + + 程 序 的 执 行 过 程 ,掌 握 C ++ 语 言 的 语 法
特 征 ; 熟 练 掌 握 在 V i s u a l C + + 开 发 环 境 下 独 立 编 写 和 调 试 C + +程
序;掌握面向对象程序和泛型程序设计思想和技术,建立起用面
向对象思维和泛型思维进行问题分析和程序设计的习惯;掌握用
C + + 语 言 解 决 实 际 问 题 的 方 法 ,建 立 优 秀 的 面 向 对 象 编 程 和 泛 型 编
程风格,为进行大型程序设计和科研编程打下基础。
二、学时分配
6 个实验共 36 学时
实验名称
实 验 一 、 C++程 序 设 计 基 础 与 开 发 环 境
实验二、类与对象
实验三、运算符重载
实验四、继承机制、虚基类、虚函数与运行时多态
实 验 五 、 模 板 、 STL 与 泛 型 编 程
实 验 六 、 异 常 处 理 、 C ++ I / O 流
学时分配
4
6
6
8
8
4
1
三、实验目的和实验内容
实 验 一 、 C++程 序 设 计 基 础 与 开 发 环 境
【实验目的】
1. 通 过 实 验 体 会 C++在 面 向 过 程 程 序 设 计 方 面 与 C 的 异 同
2. 初 步 了 解 C+ +的 程 序 设 计 风 格
3. 掌 握 函 数 重 载 和 命 名 空 间
4 . 进 一 步 熟 悉 Vi s u a l C + + 集 成 开 发 环 境
【实验内容】
1. 编 写 一 个 完 整 的 面 向 过 程 风 格 的 C+ +程 序 , 满 足 以 下 条 件 :
1) 三 个 重 载 函 数 , 分 别 接 收 两 个 整 数 、 两 个 浮 点 数 和 两 个 字 符 串 做
参 数 (引 用 类 型 ), 实 现 两 个 数 互 相 交 换 。
2) 在 主 函 数 中 输 入 交 换 前 和 输 出 交 换 后 的 两 个 数 , 实 现 两 数 值 交
换。
3) 主 函 数 应 给 出 必 要 的 输 入 提 示 。
2 . 有 以 下 n a me s p a c e 定 义 ,
n a me s p a c e m y n s p a c e
{
const int SIZE = 100;
int A[SIZE];
int mode( int [ ] , int) ;
};
m o d e 函 数 的 功 能 是 输 出 数 组 中 的 众 数 ,及 其 出 现 的 次 数 ,其 返 回 值
是 众 数 的 个 数 。 完 成 my n s p a c e 的 定 义 , 并 在 ma i n 函 数 中 写 出 完 整
程 序 , 演 示 函 数 的 功 能 ( 将 my n s p a c e : : A 作 为 实 参 传 给 mo d e 函 数 ) 。
3 .分 别 写 C 和 C + + 程 序 ,将 文 本 文 件 o l d . t x t 中 的 所 有 内 容 拷 贝 到 新 文
件 n e w. t x t 中 。
4. 输 入 下 面 的 程 序 , 用 编 译 器 的 单 步 调 试 功 能 观 察 其 输 出 , 说 明 为 什
么 ? 如 果 把 注 释 语 句 中 的 注 释 符 号 “ //” 去 掉 , 程 序 能 运 行 吗 ? 试
解释原因。
# i n c l u d e < i o s t r e a m>
using na mespa ce std;
2
n a me s p a c e a l i p
{
int a i=16, aj=15, a k=23;
}
int aj=0;
v o i d ma i n i p ( )
{
//cout<<"a i:"<<a i<<endl;
using na mespa ce a lip;
++ai;
cout<<"a i:"<<a i<<endl;
//++aj;
++::a j;
cout<<"::a j:"<<::a j<<endl;
++alip::aj;
cout<<"a lip::a j:"<<a lip::a j<<endl;
cout<<"ak:"<<ak<<endl;
int ak=97;
cout<<"ak:"<<ak<<endl;
++ak;
cout<<"ak:"<<ak<<endl;
}
n a me s p a c e b l i p
{
int bi=16, bj=15, bk=23;
}
int bj=0;
//int worongInit=bk;
i n t ma i n ( )
{
c o u t < < " ma i n ( ) o u t p u t s t a r t i n g " < < e n d l ;
//cout<<"bi:"<<bi<<endl;
using blip::bi;
++bi;
cout<<"bi:"<<bi<<endl;
cout<<"bj:"<<bj<<endl;
using blip::bj;
++bj;
cout<<"bj:"<<bj<<endl;
int bk;
cout<<"bk:"<<bk<<endl;
//using blip::bk;
3
c o u t < < " ma i n i p ( ) o u t p u t s t a r t i n g " < < e n d l ;
ma i n i p ( ) ;
return 0;
}
实验二、类与对象
【实验目的】
1. 学 习 类 的 定 义 和 使 用 方 法
2. 学 习 对 象 的 声 明 和 使 用
3. 学 习 对 象 构 造 中 , 默 认 构 造 函 数 、 拷 贝 构 造 函 数 、 类 型 转 换 构 造 函
数和普通多参数构造函数的定义和使用
4. 类 静 态 成 员 的 定 义 和 使 用
5. 体 会 深 拷 贝 和 浅 拷 贝 的 区 别
6 .使 用 Vi s u a l C + + 的 D e b u g 功 能 ,观 察 程 序 流 程 ,观 察 类 的 构 造 函 数 、
析构函数的执行顺序和过程
7. 体 会 基 于 对 象 程 序 设 计 方 法 和 面 向 过 程 程 序 设 计 方 法 的 区 别
【实验内容】
1. 完 成 以 下 类 定 义 :
cla s s s er ies Co mp {
int n; //n 大 于 或 等 于 1
ser iesComp( int n) ;
i n t s u m( ) ;
int fib();
double taylor(double x);
};
其 中 s u m( ) 计 算 1 + 2 + 3 + … + n ;
fib()计 算 斐 波 纳 契 (Fibonacci)数 列 前 n 项 和 ;
2
n
t a y l o r ( ) 计 算 1 − x + x − ... + ( −1) n x
1!
2!
n!
2.定 义 一 个 类 ,确 保 该 类 实 例 化 的 对 象 数 目 最 多 只 能 有 一 个 (提 示 ,使
4
用 sta tic 数 据 成 员 和 成 员 函 数 ) 。
3. J osephus 问 题 : n 个 人 围 坐 在 一 圈 , 现 从 指 定 的 第 s 个 人 开 始 报 数 ,
数 到 第 m 个 人 出 列 ,然 后 从 出 列 的 下 一 个 人 重 新 开 始 报 数 ,数 到 第
m 个人又出列,如此重复,直到所有的人全部出列为止。
定 义 一 个 链 表 类 和 J osephus 类 。 编 制 应 用 程 序 , 给 出 人 数 n, 起 始
位 置 s 和 数 人 数 m, 解 决 J o s e p h u s 问 题 。 仔 细 体 会 你 的 方 法 与 面 向
过程的解决方法的区别。
4.定 义 一 个 名 为 Date 的 类 ,用 于 输 入 并 验 证 日 期 ,类 中 的 数 据 成 员 和
成员函数应满足下面的规则;在主函数中编写相应代码验证这些规
则 是 否 正 确 地 实 现 了 ; 并 用 Debug 功 能 仔 细 体 会 程 序 的 运 行 过 程 。
1 ) 日 期 包 括 年 ( y e a r ) 、 月 ( mo n t h ) 、 日 ( d a y ) , 均 为 整 型 数 据 , 以 及 一
个 布 尔 数 据 成 员 pa ss 用 来 判 断 输 入 日 期 是 否 正 确 。
2 ) 布 尔 类 型 p r i v a t e 成 员 函 数 c h e c k F o r ma t ( ) , 有 一 个 字 符 串 引 用 形
参 , 验 证 读 入 的 日 期 格 式 是 否 正 确 ( yyyy: mm:dd 格 式 ) ?
3) void 类 型 private 成 员 函 数 validate(), 没 有 形 参 。 判 断 输 入 的 日
期是否合法。
4) 默 认 构 造 函 数 , 没 有 形 参 , 将 yea r , mont h, da y 分 别 初 始 化 为
2 0 0 6 , 1 , 1 ; 在 构 造 函 数 中 要 调 用 v a l i d a t e ( )。
5) 定 义 拷 贝 构 造 函 数
6) 定 义 类 型 转 换 构 造 函 数 , 能 从 字 符 串 转 换 成 Date 对 象 。
7 ) 使 用 带 默 认 参 数 的 构 造 函 数 , 三 个 整 形 形 参 i n t y , i n t m= 2 , i n t
d=29, 分 别 对 yea r, mont h, da y 赋 值 ; 注 意 要 调 用 va lida te( )
验证。
8) void 类 型 public 成 员 函 数 setDate(), 有 一 个 字 符 串 引 用 形 参 , 负
责 设 置 新 日 期 ,首 先 调 用 c h e c k F o r ma t ( ) 判 断 日 期 数 据 格 式 是 否 正
确 , 如 正 确 , 继 续 调 用 va lida te( )验 证 输 入 日 期
9) void 类 型 public 成 员 函 数 pr intD a ta( ), 没 有 形 参 , 根 据 pa ss 值
决 定 是 否 打 印 设 置 的 日 期 , 如 果 打 印 , 要 按 照 ” yyyy 年 mm 月 dd
日 ”的 格 式 。
实验三、运算符重载
【实验目的】
1. 掌 握 常 用 运 算 符 重 载 的 定 义 方 法
2. 掌 握 运 算 符 重 载 的 两 种 方 式 : 成 员 函 数 和 友 元 函 数
3. 体 会 运 算 符 重 载 对 构 建 ADT 的 作 用
5
【实验内容】
1 . 参 考 教 材 第 11 章 的 例 子 。 实 现 数 学 中 的 复 数 类 C o m p l e x , 它 能 实 现
普 通 的 +、 -、 *、 /数 学 运 算 , =、 +=、 -=、 *=、 /=赋 值 运 算 , 前 后 序
的 自 增 自 减 ( + + a , a + + , - - a , a - - ) 运 算 , = = 、! = 关 系 运 算 。
2 . 以 下 类 定 义 代 码 段 , Ve c t o r 是 一 个 简 单 的 向 量 类 , v e c t o r C o n t a i n e r
向 量 容 器 类 可 以 包 含 多 个 向 量 , 它 重 载 了 [],->,()三 个 运 算 符 。 试 完
成 类 定 义 , 使 得 类 vectorContainer 的 对 象 VC 表 现 出 以 下 行 为 :
VC[i]; //打 印 VC 中 第 i 个 向 量 的 所 有 元 素 , i 为 整 数 ,下 同
V C - > o u t p u t ( ) ; / / 打 印 V C 中 某 个 向 量 所 有 元 素 及 其 元 素 和 ,该 向 量
的元素和在所有向量元素和中是最大的
VC(); //把 VC 中 的 向 量 按 照 其 元 素 和 大 小 排 序 , 然 后 顺 序 输 出 向
量及其元素和
V C ( i ) ; / / 把 V C 中 的 向 量 按 照 其 元 素 和 由 小 到 大 排 序 ,然 后 输 出 第
i 个向量所有元素及其元素和
VC( i, j) ;//输 出 第 i 个 向 量 的 第 j 个 元 素 。
思考以下问题:
Ve c t o r 类 和 v e c t o r C o n t a i n e r 类 有 良 好 的 “ 封 装 性 ” 吗 ?
如 果 v e c t o r C o n t a i n e r 不 是 Ve c t o r 的 友 元 , 代 码 应 如 何 改 动 ?
.
c l a s s Ve c t o r
{
float v[4];
//…
friend class vect orContainer;
public:
Ve c t o r ( f l o a t v e [ ] ) {
for ( int i=0; i<4; i++)
v[i]=ve[i];
}
Ve c t o r ( ) {
for ( int i=0; i<4; i++)
v[i]=0;
}
~ Ve c t o r ( ) { }
voi d out put();
}
};
class vectorContainer
{
6
Ve c t o r * _ v ;
int _size;
//…
public:
v e c t o r C o n t a i n e r ( Ve c t o r * v e , i n t s i z e ) {
_ v = n e w Ve c t o r [ s i z e ] ;
_siz e = siz e;
for ( int i=0; i<siz e; i++)
_v[ i] =ve[ i] ;
}
vect orContainer();
~vectorContainer();
Ve c t o r & o p e r a t o r [ ] ( i n t i d x ) ;
Ve c t o r * o p e r a t o r - > ( ) ;
voi d operator()();
Ve c t o r & o p e r a t o r ( ) ( i n t i ) ;
float& operator()(int i, int j);
};
实验四、继承机制、虚基类、虚函数与运行时多态
【实验目的】
1. 学 习 类 的 继 承 关 系 , 声 明 派 生 类
2. 熟 悉 不 同 继 承 方 式 下 派 生 类 对 基 类 成 员 的 访 问 限 制
3. 学 习 多 继 承 的 原 理 , 虚 基 类 的 用 途 及 其 使 用 方 法
4. 虚 函 数 的 功 能 , 虚 函 数 支 持 面 向 对 象 多 态 机 制 的 工 作 原 理
5. 学 习 纯 虚 函 数 与 抽 象 基 类 的 定 义 以 及 其 作 用 , 理 解 抽 象 基 类 和 具 体
类的区别。
6. 基 类 与 派 生 类 构 造 函 数 和 释 构 函 数 的 调 用 顺 序 。
7. 理 解 基 本 的 设 计 模 式
【实验内容】
7
1. 编 写 一 个 基 类 , 声 明 有 public、 protect ed、 private 成 员 , 然 后 使 用
public 继 承 , protected 继 承 , private 继 承 分 别 生 成 派 生 类 , 观 察 派
生类对基类不同类型成员的访问,验证以下继承规则:
1 ) A p u b l i c me m b e r i s a c c e s s i b l e f r o m a n y w h e r e w i t h i n a p r o g r a m.
2 ) A p r i v a t e me m b e r c a n b e a c c e s s e d o n l y b y t h e m e mb e r f u n c t i o n s a n d
friends of its class.
3 ) A p ro t e c t e d m e mb e r b e h a v e s a s a p u b l i c m e m b e r t o a d e r i v e d c l a s s ,
a n d b e h a v e s a s a p r i v a t e me mb e r t o t h e r e s t o f t h e p r o g r a m.
2. 有 类 定 义
class base
{
public:
v i r t u a l v o i d i a m( ) { c o u t < < " b a s e \ n " ; }
};
从 类 b a s e 派 生 两 个 类 , 每 个 派 生 类 定 义 函 数 i a m( ) 输 出 派 生 类 的 名
字。
创 建 这 些 类 的 对 象 , 并 分 别 通 过 这 些 对 象 调 用 i a m( ) 。
把 指 向 两 个 继 承 类 对 象 的 指 针 分 别 赋 值 给 base*指 针 , 通 过 这 些 指 针 调
用 i a m( ) 。
3. 防 真 农 场 , 一 个 哺 乳 动 物 类 如 下 所 示 :
# i n c l u d e < i o s t r e a m. h >
c l a s s M a m ma l
{
public:
M a m ma l ( ) : i t s A g e ( 2 ) , i t s We i g h t ( 5 )
{
c o u t < < ” M a m ma l c o n s t r u c t o r … \ n ” ;
}
~ M a m ma l ( ) { c o u t < < ” M a m ma l d e s t r u c t o r … \ n ” ; }
int GetAge()const {return itsAge;}
voi d Set Age(int age) {itsAge=age;}
i n t G e t We i g h t ( ) c o n s t { r e t u r n i t s We i g h t ; }
8
v o i d S e t We i g h t ( i n t w e i g h t ) { i t s We i g h t = w e i g h t ; }
v o i d M o v e ( ) c o n s t { c o u t < < ” M a m ma l m o v e o n e s t e p \ n ” ; }
v o i d S p e a k ( ) c o n s t { c o u t < < ” M a m ma l s p e a k ! \ n ” ; }
voi d sleep() const {cout <<”shhh,I’ m sleepi ng.\ n”;}
protect ed:
int itsAge;
i n t i t s We i g h t ;
};
1) 狗 属 哺 乳 动 物 , 且 它 的 属 性 有 品 种 之 分 ( 在 哺 乳 类 基 础 上 增 加 品
种 数 据 成 员 ), 叫 声 区 别 于 其 它 动 物 ( S p e a k ( ) , 输 出 ” Wo o f ! ”) ,
还 会 摇 尾 巴 ( 增 加 成 员 函 数 ,输 出 ”Ta i l w a g g i n g … ”) , 乞 讨 食 物 ( 增
加 成 员 函 数 , 输 出 ” bagging for food… ” )。
2 ) 驴 、马 、猪 也 属 哺 乳 动 物 ,其 叫 声 分 别 为 :
“ M e o w ! ”, ”W i n n i e ! ”, ”
O i n k ! ”。
3 ) 骡 既 是 驴 , 也 是 马 , 叫 声 为 ” mu l e ” 。
4) 编 程 分 别 使 各 个 动 物 表 现 为 不 一 样 的 行 为 ,并 观 察 基 类 与 派 生 类
的构造函数与释构函数的调用顺序。
5 ) 把 M a m ma l 中 的 非 构 造 、 非 释 构 成 员 函 数 改 为 虚 函 数 , 然 后 派 生
不 同 的 动 物 类 ,编 写 程 序 说 明 虚 函 数 的 作 用( 与 普 通 成 员 函 数 相
比 较 )。
6 ) 如 果 M a ma l 中 的 非 构 造 、非 释 构 成 员 函 数 中 有 纯 虚 函 数 ,派 生 不
同 的 动 物 类 ( 具 体 类 ), 编 写 程 序 声 明 对 象 体 会 抽 象 类 和 具 体 类
的区别。
4 . 建 立 一 个 中 山 大 学 学 生 基 类 sysuStu de nt, 有 注 册 交 费 r eg( )、 选 课
s e l e c t ( ) 等 成 员 函 数 ;派 生 出 软 件 学 院 学 生 类 s s S t u d e n t ,管 理 学 院 学
生 类 ms S t u d e n t ; 从 s s S t u d e n t 和 ms S t u d e n t 派 生 出 双 学 位 学 生
dd Stu de nt。 它 们 都 定 义 了 r eg( )和 select( )成 员 函 数 , 编 写 程 序 体 验
虚基类、虚函数的作用。
5*. 定 义 类 , 满 足 以 下 基 本 需 求 :
1) 该 类 主 要 功 能 是 对 数 组 进 行 排 序 ;
2) 排 序 的 算 法 有 简 单 选 择 排 序 、 冒 泡 排 序 等 , 可 选 ;
3)类 设 计 尽 量 满 足“ 良 好 的 可 扩 展 性 ”要 求 ,也 就 是 类 功 能 有 良 好
的可扩充性,比如排序算法还可以添加插入排序、快速排序等。
6* .某 市 某 产 鞋 乡 镇 企 业 ,需 要 开 发 一 套 管 理 软 件 ,对 企 业 的 产 鞋 过 程
进 行 管 理 。在 该 企 业 中 ,生 产 一 种 鞋 需 要 一 条 生 产 线 ( 比 如 皮 鞋 需 要
9
一 种 生 产 线 ,运 动 鞋 需 要 另 一 种 )。该 企 业 随 着 业 务 的 不 断 发 展 ,会
不断引进新生产线,生产新品种的鞋,所以企业希望开发的软件能
够不断适应这个需求。你能用面向对象的程序设计思想,模拟该软
件的核心功能实现吗?
实 验 五 、 模 板 、 STL 与 泛 型 编 程
【实验目的】
1. 掌 握 C+ +中 的 模 板 机 制 对 泛 型 编 程 思 想 的 支 持
2. 掌 握 泛 型 函 数 、 泛 型 类 的 定 义 及 其 使 用 方 法
3. 熟 悉 STL Sequence 和 STL Associative 容 器 类 的 使 用 方 法
4. 练 习 STL 通 用 算 法 、 函 数 对 象 和 函 数 适 配 器 的 使 用
【实验内容】
1. 用 模 板 机 制 实 现 泛 型 函 数 , 重 写 实 验 一 的 第 一 题 , 功 能 要 求 不 变 。
2 . 泛 型 化 实 验 三 的 c o mp l e x 类 , 使 其 能 处 理 不 同 的 数 据 类 型 的 数 据 。
3 . 下 面 的 源 代 码 是 模 拟 STL list 的 实 现 , 试 仔 细 体 会 链 表 的 性 质 和
I t e r a t o r 的 实 现 方 法 ( 请 补 充 完 成 未 定 义 的 成 员 函 数 )。 并 编 写 程 序
比 较 这 个 实 现 和 标 准 STL list 实 现 的 异 同 。
/********************************************************
************/
# i n c l u d e < i o s t r e a m>
#include <cassert>
using na mespa ce std;
class list {
public:
s t r u c t l i s t e l e m;
//forward declarations
cla s s itera tor ;
fr i e n d i t e r a t o r ;
list() :h( 0) , t( 0) {}//constr uct the e mpt y list
list( siz e_t n_elements,
char c);
list( const list& x) ;
lis t( iter a tor b , iter a tor e) ;
~list( ) { r elea se() ; }
itera tor b egin( ) cons t { r etur n h; }
itera tor end( ) cons t { r etur n t;}
void push_ front(char c);
10
void pop_front();
char & fr ont( ) {r etur n h - > da ta ;}
char & ba ck( ) {r etur n t - > da ta ;}
bool e mpty( ) const{ r etur n h == 0;}
void release();
fr i e n d o s t r e a m& o p e r a t o r < < ( o s t r e a m& o u t , l i s t & x ) ;
struct listelem
//list cell
{
char
da ta ;
listelem* next, *pr e v;
l i s t e l e m( c h a r c , l i s t e l e m * n , l i s t e l e m * p )
:da ta( c), next( n) , pr ev( p) {}
};
//scope d wit hi n cla ss list
cla s s itera tor {
public:
i t e r a t o r ( l i s t e l e m* p = 0 ) : p t r ( p ) { }
iterator operator++();
iterator operator--();
iterator operator++(int);
iterator operator--(int);
listelem* oper a tor- >() {r etur n ptr ;};
cha r & oper a tor *( ) {r etur n ptr -> da ta ;}
o p e r a t o r l i s t e l e m* ( ) { r e t u r n p t r ; } / / c o n v e r s i o n
private:
listelem* ptr ;//cur r ent listele m or 0
};
private:
l i s t e l e m* h , * t ;
//head
and tail
};
l i s t : : l i s t ( s i z e _ t n _ e l e me n t s ,
char c)
{
a s s er t( n_ elements > 0 ) ;
h = t = 0;
f o r ( s i z e _ t i = 0 ; i < n _ e l e me n t s ; + + i )
push_front(c);
}
list::list(const list& x)
{
list::itera tor r = x.begin( ) ;
11
h = t = 0 ; / / n e e d e d f o r e mp t y l i s t
while (r != 0)
push_front(*r++);
}
voi d list::release()
{
while (h != 0)
pop_front();
}
o s t r e a m& o p e r a t o r < < ( o s t r e a m& o u t , l i s t & x )
{
list::itera tor p = x.begin( ) ; //gets x.h
out << "list = (";
while (p != 0) {
out << *p
<< ","; //gets a char&
++p; //advances iterator using next
}
cout << ")\n";
return out;
}
voi d list::pus h_front(char c)
{
l i s t e l e m* t e m p = n e w l i s t e l e m ( c , h , 0 ) ;
if (h != 0) {
//wa s a none mpt y list
h - > p r e v = t e mp ;
temp - > next = h;
h = temp;
}
else
//was a n e mpty l ist
h = t = temp;
}
voi d list::pop_front()
{
l i s t e l e m* t e m p = h ;
if (h != 0) {
//wa s a none mpt y list
h = h -> next;
delete te mp;
12
}
else
//was a n e mpty l ist
cout << "empt y list!" <<endl;
}
list::iterator list::iterator::operator++()
{
a s s er t( ptr != 0) ;
ptr = ptr -> next;
return *this;
}
list::iterator
lis t::itera tor ::oper a tor + +( int)
{
a s s er t( ptr != 0) ;
itera tor temp = *t his ;
ptr = ptr -> next;
r etur n te mp;
}
i n t ma i n ( )
{
list l1;
l1.push_ front('A');
l1.push_ front('F');
l1.push_ front('C');
l1.push_ front('z');
l1.push_ front('l');
cout <<
return 0;
l1;
}
4 .理 解 教 材 给 出 的 S e q u e n c e 容 器 成 员 类 型 ,S e q u e n c e 容 器 成 员 函 数 用
法 。分 别 编 写 程 序 ,演 示 S e q u e n c e 容 器 v e c t o r 、l i s t 、d e q u e 的 构 造 、
插入、删除、访问、赋值、交换操作。
5 .理 解 教 材 给 出 的 A s s o c i a t i v e 容 器 成 员 类 型 ,A s s o c i a t i v e 容 器 成 员 函
数 功 能 。 分 别 编 写 程 序 , 演 示 A s s o c i a t i v e 容 器 s e t 、 ma p 、 mu l t i s e t 、
m u l t i ma p 的 构 造 、 插 入 、 删 除 、 访 问 、 赋 值 、 交 换 操 作 。
6 . U s i n g a r a n d o m n u mb e r g e n e r a t o r, g e n e r a t e 1 0 , 0 0 0 i n t e g e r s b e t w e e n 0
a n d 9 , 9 9 9 . P l a c e t h e m i n a l i s t < i n t > c o n t a i n e r. C o m p u t e a n d p r i n t t h e
me dia n va lue. W ha t wa s expected? Comput e the fr equencies of ea ch
v a l u e ; i n o t h e r w o r d s , h o w ma n y 0 s w e r e g e n e r a t e d , h o w ma n y 1 s w e r e
g e n e r a t e d , a n d s o f o r t h . P r i n t t h e v a l u e w i t h g r e a t e s t f r e q u e n c y. U s e a
13
vect or<int> to store t he frequencies. Be noticed t hat you s houl d use
algorithm, function object and function adapter.
实 验 六 、 异 常 处 理 、 C++ I/O 流
【实验目的】
1. 理 解 C+ +语 言 的 异 常 抛 出 处 理 机 制
2. 学 习 异 常 处 理 的 声 明 和 执 行 过 程
3. 学 习 C+ +中 的 格 式 化 I/O
4. 掌 握 C+ +文 件 I/O 流
【实验内容】
1. 编 写 计 算 整 数 阶 乘 的 程 序 , 用 异 常 抛 出 处 理 机 制 进 行 输 入 、 溢 出 时
异常的处理。在主程序中设置不同条件,观察程序的处理流程。
2 .S u p p o s e y o u a r e g i v e n l i n e - o r i e n t e d d a t a i n a f i l e f o r ma t t e d a s f o l l o w s :
Australia
5E56,7667230284,Langler,Tys on,31.2147,0.00042117361
2B97,7586701,Oneill,Zeke,553.429,0.0074673053156065
4D75,7907252710,Nickerson,Kelly,761.612,0.010276276
9F2,6882945012,Hartenbach,Neil,47.9637,0.0006471644
Austria
480F,7187262472,Oneill,Dee,264.012,0.00356226040013
1 B 6 5 , 4 7 5 4 7 3 2 6 2 8 , H a n e y , K i m, 7 . 3 3 8 4 3 , 0 . 0 0 0 0 9 9 0 1 5 9 4 8 4 7 5
DA1,1954960784,Pascente,Lester,56.5452,0.0007629529
3F18,1839715659,Elsea,Chelsy,801.901,0.010819887645
Belgi um
BDF,5993489554,Oneill,Meredith,283.404,0.0038239127
5AC6,6612945602,Parisienne,Biff,557.74,0.0075254727
6AD,6477082,Penni ngt on,Lizanne,31.0807,0.0004193544
4 D 0E , 7 86 1 65 2 68 8 , Sis ca , Fra ncis , 7 0 4. 7 5 1, 0 .0 0 9 5 09 0 62 3 8
B a h a ma s
3 7 D 8 , 6 8 3 7 4 2 4 2 0 8 , P a r i s i e n n e , S a ms o n , 3 9 6 . 1 0 4 , 0 . 0 0 5 3 4 4 5
5 E 9 8 , 6 3 8 4 0 6 9 , W i l l i s , P a m, 9 0 . 4 2 5 7 , 0 . 0 0 1 2 2 0 0 9 5 6 4 0 5 9 2 4 6
1462,1288616408,Stover ,H az a l,583.939,0.007878970561
5 F F 3 , 8 0 2 8 7 7 5 7 1 8 , S t r o ms t e d t , B u n k , 3 9 . 8 7 1 2 , 0 . 0 0 0 5 3 7 9 7 4
1095,3737212,Stover,Denny,3.05387,0.000041205248883
7428,2019381883,Parisienne,S hane,363.272,0.00490155
14
The heading of each section is a region, and every line under that heading is a seller
in that region. Each comma-separated field represents the data about each seller. The
first field in a line is the SELLER_ID which unfortunately was written out in
hexadecimal format. The second is the PHONE_NUMBER (notice that some are
missing area codes). LAST_NAME and FIRST_NAME then follow.
TOTAL_SALES is the second to the last column. The last column is the decimal
amount of the total sales that the seller represents for the company. You are to format
the data on the terminal window so that an executive can easily interpret the trends.
Sample output is given below.
Austr a lia
--------------------------------* L a s t N a me *
Langler
Oneill
* F i r s t N a me *
*ID*
Ty s o n
24150
Zeke
111 5 9
*Phone*
*Sales*
*Percent*
766-723-0284
31.24
4.21E-02
XXX-758-6701
553.43 7.47E-01
3. 有 磁 盘 文 件 student.txt, 文 件 记 录 格 式 为 :
学号
姓名
性别
出生年月
生源地
高考成绩
修 改 实 验 五 (3)的 链 表 结 构 , 使 其 结 点 能 存 储 student.txt 的 记 录 。 并
在 相 应 成 员 函 数 里 加 入 异 常 抛 出 处 理 代 码 。 用 student.txt 的 所 有 数
据初始化列表。列表能实现按照高考程序排序,排序后的所有学生
纪 录 输 出 到 student_sort.txt 中 。
附 录 VC 程 序 调 试 (本 部 分 录 自 网 上 资 料 )
在开发程序的过程中,经常需要查找程序中的错误,这就需要利用
调试工具来帮助你进行程序的调试,当然目前有许多调试工具,而集成
在 V C 中 的 调 试 工 具 以 其 强 大 的 功 能 ,一 定 使 你 爱 不 释 手 。下 面 我 们 先 来
介 绍 VC 中 的 调 试 工 具 的 使 用 。
1
VC 调 试 工 具
1.1 调试环境的建立
在 VC 中 每 当 建 立 一 个 工 程 (Project)时 ,VC 都 会 自 动 建 立 两 个 版 本 :
R e l e a s e 版 本 , 和 D e b u g 版 本 , 正 如 其 字 面 意 思 所 说 的 ,R e l e a s e 版 本 是 当
程 序 完 成 后 ,准 备 发 行 时 用 来 编 译 的 版 本 ,而 Debug 版 本 是 用 在 开 发 过 程
中进行调试时所用的版本。
15
DEBUG 版 本 当 中 ,包 含 着 MICROSOFT 格 式 的 调 试 信 息 ,不 进 行 任 何
代 码 优 化 , 而 在 RELEASE 版 本 对 可 执 行 程 序 的 二 进 制 代 码 进 行 了 优 化 ,
但是其中不包含任何的调试信息。
在 新 建 立 的 工 程 中 , 你 所 看 到 是 DEBUG 版 本 , 若 要 选 择 RELEASE 版
本 ,可 以 选 择 菜 单 P R O J E C T 中 的 S E T T I N G 命 令 ,这 时 屏 幕 上 面 弹 出 P R O J E C T
SETTEING 对 话 框 , 在 SETTING FOR 下 拉 列 表 中 选 择 RELEASE, 按 OK 退
出 ,如 图 4.1。
图 4.1
在 调 试 程 序 的 时 候 必 须 使 用 DEBUG 版 本 , 我 们 可 以 在
Setting 对 话 框 的 C/C++页 中 设 置 调 试 选 项 。
Project
16
图 4.2
各个选项的含意如下:
• Program Database 表 示 产 生 一 个 存 储 程 序 信 息 的 数 据 文 件 (.PDB),
它包含了类型信息和符号化的调试信息;
• Line Numbers Only 表 示 程 序 经 过 编 译 和 链 接 产 生 的 .OBJ 或 .EXE
文件仅仅包含全局和外部符号以及行号信息;
• C7 Compatible 表 示 产 生 一 个 .OBJ 或 .EXE 文 件 行 号 信 息 以 及 符 号
化的调试信息;
• None 表 示 不 产 生 任 何 调 试 信 息 。
1.2 调试的一般过程
调试,说到底就是在程序的运行过程的某一阶段观测程序的状态,
而在一般情况下程序是连续运行的,所以我们必须使程序在某一地点停
下 来 。 所 以 我 们 所 做 的 第 一 项 工 作 就 是 设 立 断 点 。 其 次 ,再 运 行 程 序 ,当
程 序 在 设 立 断 点 处 停 下 来 时 , 再 利 用 各 种 工 具 观 察 程 序 的 状 态 。程 序 在 断
点停下来后,有时我们需要按我们的要求控制程序的运行,以进一步观
测程序的流向,所以下面我们依次来介绍断点的设置,如何控制程序的
运行以及各种观察工具的利用。
1.3 如何设置断点
在 VC 中 , 你 可 以 设 置 多 种 类 型 的 断 点 , 我 们 可 以 根 据 断 点 起 作 用 的
方 式 把 这 些 断 点 分 为 三 类 :1、 与 位 置 有 关 的 断 点 ;2、与 逻 辑 条 件 有 关 的
断 点 3、 与 WINDOWS 消 息 有 关 的 断 点 下 面 我 们 分 别 介 绍 这 三 类 断 点 。
首先我们介绍与位置有关的断点。
17
1、 最 简 单 的 是 设 置 一 般 位 置 断 点 , 你 只 要 把 光 标 移 到 你 要 设 断 点 的
位置,当然这一行必须包含一条有效语句的;然后按工具条上的
add/remove breakpoint 按 钮 或 按 快 捷 键 F9; 这 时 你 将 会 在 屏 幕
上看到在这一行的左边出现一个红色的圆点表示这二设 立了一
个断点。
图 4.3
2 、有的时候你可能并不需要程序每次运行到这儿都停下来,而是
在满足一定条件的情况下才停下来,这时你就需要设置一种与位置有关
的 逻 辑 断 点 。 要 设 置 这 种 断 点 我 们 只 需 要 从 EDIT 菜 单 中 选 中
breakpoint 命 令 , 这 时 Breakpoint 对 话 框 将 会 出 现 在 屏 幕 上 。 选 中
Breakpoint 对 话 框 中 的 LOCATION 标 签 , 使 LOCATION 页 面 弹 出 , 如 图
4.4
18
图 4.4
单 击 c o n d i t i o n 按 钮 ,弹 出 B r e a k p o i n t 对 话 框 ,在 E x p r e s s i o n 编 辑
框 中 写 出 你 的 逻 辑 表 达 式 , 如 X>=3 或 a+b>25,最 后 按 OK 返 回 。
图 4.5
这种断点主要是由其位置发生作用的,但也结合了逻辑条件,使之
更灵活。
3 、有 时 我 们 需 要 更 深 入 地 调 试 程 序 ,我 们 需 要 进 入 程 序 的 汇 编 代 码 ,
因此我们需要在在汇编代码上设立断点:要设立这种断点我们只需从
View 菜 单 中 选 Debug window 命 令 ,
19
图 4.6
再 选 Disassembly 子 命 令 , 这 时 汇 编 窗 口 将 会 出 现 在 屏 幕 上 。
图 4.7
在 图 4 . 7 中 的 汇 编 窗 口 中 你 将 看 到 对 应 于 源 程 序 的 汇 编 代 码 ,其 中 源
程序是用黑体字显示,下面是且对应的汇编代码。要设立断点,我们只
需 将 光 标 移 到 你 想 设 断 点 处 然 后 点 击 工 具 条 上 的 Insert/Remove
Breakpoints 按 钮 , 此 后 你 将 会 看 到 一 个 红 圆 点 出 现 在 该 汇 编 代 码 的 右
边。
20
图 4.8
上 面 所 讲 的 断 点 主 要 是 由 于 其 位 置 发 挥 作 用 的 ,即 当 程 序 运 行 到 设 立
断点的地方时程序将会停下来。但有时我们设立只与逻辑条件有关的断
点,而与位置无关。所以下面介绍一下与逻辑条件有关的断点。
( 1) 逻 辑 条 件 触 发 断 点 的 设 置 :
l 从 EDIT 菜 单 中 选 中 breakpoint 命 令 , 这 时 屏 幕 上 将 会 出 现
Breakpoint 对 话 框 。
图 4.9
l
选 中 Breakpoint 对 话 框 中 的 DATA 标 签 , 对 应 的 页 面 将 会 弹
出
21
l
图 4.10
在 图 4.10 的 DATA 页 面 中 的 Expression 编 辑 框 中 写 出 你 的 逻
辑 表 达 式 , 如 (X==3);
图 4.11
l 最 后 按 OK 返 回 。
其他几种断点的设置的方法都与之类似。我们一一加以说明。
( 2) 监 视 表 达 式 发 生 变 化 断 点 :
l 从 EDIT 菜 单 中 选 中 breakpoint 命 令 , 这 时 屏 幕 上 将 会 出 现
Breakpoint 对 话 框 。
l 选 中 Breakpoint 对 话 框 中 的 DATA 标 签 , 对 应 的 页 面 将 会 弹
出
l 在 Expression 编 辑 框 中 写 出 你 需 要 监 视 的 表 达 式
l 最 后 按 OK 键 返 回 。
( 3) 监 视 数 组 发 生 变 化 的 断 点 :
l 从 E D I T 菜 单 中 选 中 b r e a k p o i n t 命 令 ,这 时 屏 幕 上 将 会 出 现
Breakpoint 对 话 框 。
l 选 中 Breakpoint 对 话 框 中 的 DATA 标 签 , 对 应 的 页 面 将 会 弹
出
l 在 Expression 编 辑 框 中 写 出 你 需 要 监 视 数 组 名 ;
22
l
在 Number of
Elements 编 辑 框 输 入 你 需 要 监 视 数 组 元 素 的
个数;
l 按 OK 键 返 回 。
( 4) 监 视 由 指 针 指 向 的 数 组 发 生 变 化 的 断 点 :
l 从 EDIT 菜 单 中 选 中 breakpoint 命 令 , 这 时 在 屏 幕 上 将 会 出
现 B r e ak p o i n t 对 话 框 。
l 选 中 Breakpoint 对 话 框 中 的 DATA 标 签 ;
l 在 Expression 编 辑 框 中 输 入 形 如 *pointname,其 中
*pointname 为 指 针 变 量 名 ;
l 在 Number of
Elements 编 辑 框 输 入 你 需 要 监 视 数 组 元 素 的
个数;
l 按 OK 键 返 回 。
( 5) 监 视 外 部 变 量 发 生 变 化 的 断 点 :
l 从 EDIT 菜 单 中 选 中 breakpoint 命 令 这 时 屏 幕 上 将 会 出 现
Breakpoint 对 话 框 ;
l 选 中 Breakpoint 对 话 框 中 的 DATA 标 签 ;
l 在 Expression 编 辑 框 中 输 入 变 量 名 ;
l 点 击 在 Expression 编 辑 框 的 右 边 的 下 拉 键 头 ;
l 选 取 A d v a n c e d 选 项 ,这 时 A d v a n c e d B r e a k p o i n t 对 话 框 出 现 ;
l 在 context 框 中 输 入 对 应 的 函 数 名 和 (如 果 需 要 的 话 )文 件 名 ;
l 按 OK 键 关 闭 Advanced Breakpoint 对 话 框 。
l 按 OK 键 关 闭 Breakpoints 对 话 框 。
( 6 )在 讲 了 位 置 断 点 和 逻 辑 断 点 之 后 我 们 再 讲 一 下 与 W I N D O W S 消 息
有关的断点。
注 意 : 此 类 断 点 只 能 工 作 在 x86 或 Pentium 系 统 上 。
l 从 EDIT 菜 单 中 选 中 breakpoint 命 令 , 这 时 屏 幕 上 将 会 出 现
Breakpoint 对 话 框 ;
l 选 中 Breakpoint 对 话 框 中 的 MESSAGE 标 签 , 对 应 的 页 面 将 会
弹出;
l 在 Break At WndProc 编 辑 框 中 输 入 Windows 函 数 的 名 称 ;
l 在 Set One Breakpoint From Each Message To Watch 下 拉
列表框中选择对应的消息;
l 按 OK 返 回 。
1.4 控制程序的运行
上 面 我 们 讲 了 如 何 设 置 各 类 断 点 ,下 面 我 们 来 介 绍 如 何 控 制 程 序 的 运
行 。当 我 们 从 菜 单 B u i l d 到 子 菜 单 S t a r t D e b u g i n g 选 择 G o 程 序 开 始 运
行 在 Debug 状 态 下 , 程 序 会 由 于 断 点 而 停 顿 下 来 后 , 可 以 看 到 有 一 个 小
箭头,它指向即将执行的代码。
23
图 4.12
随 后 , 我 们 就 可 以 按 要 求 来 控 制 程 序 的 运 行 :其 中 有 四 条 命 令 : S t e p
over, step Into , Step Out ,Run to Cursor。
图 4.13
在 图 4.13 中 :
Step over 的 功 能 是 运 行 当 前 箭 头 指 向 的 代 码 (只 运 行 一 条 代 码 )。
Step Into 的 功 能 是 如 果 当 前 箭 头 所 指 的 代 码 是 一 个 函 数 的 调 用 , 则
用 Step Into 进 入 该 函 数 进 行 单 步 执 行 。
Step Out 的 功 能 是 如 当 前 箭 头 所 指 向 的 代 码 是 在 某 一 函 数 内 , 用 它
使程序运行至函数返回处。
Run to Cursor 的 功 能 是 使 程 序 运 行 至 光 标 所 指 的 代 码 处 。
24
1.5 查看工具的使用
调 试 过 程 中 最 重 要 的 是 要 观 察 程 序 在 运 行 过 程 中 的 状 态 ,这 样 我 们
才能找出程序的错误之处。这里所说的状态包括各变量的值,寄存中的
值 ,内 存 中 的 值 , 堆 栈 中 的 值 , 为 此 我 们 需 要 利 用 各 种 工 具 来 帮 助 我 们
察看程序的状态。
♦ 弹 出 式 调 试 信 息 泡 泡 (Data Tips Pop_up Information)。
当程序在断点停下来后,要观察一个变量或表达式的值的最容易的
方法是利用调试信息泡泡。要看一个变量的值,只需在源程序窗口中,
将 鼠 标 放 到 该 变 量 上 , 你 将 会 看 到 一 个 信 息 泡 泡 弹 出 ,其 中 显 示 出 该 变 量
的值。
图 4.14
要 查 看 一 个 表 达 式 的 值 ,先 选 中 该 表 达 式 ,仍 后 将 鼠 标 放 到 选 中 的 表
达 式 上 ,同 样 会 看 到 一 个 信 息 泡 泡 弹 出 以 显 示 该 表 达 式 的 值 如 图 4 . 1 5 所
示。
25
图 4.15
♦ 变 量 窗 口 (VARIABLE WINDOW)。
在 VIEW 菜 单 , Debug window 选 Variables window; 变 量 窗 口 将 出
现在屏幕上。其中显示着变量名及其对应的值。你将会看到在变量观察
窗 口 的 下 部 有 三 个 标 签 : AUTO ,LOCAL,THIS 选 中 不 同 的 标 签 , 不 同 类 型
的变量将会显示在该窗口中。
图 4.16
♦ 观 察 窗 口 (WATCH WINDOW):
26
在 VIEW 菜 单 , 选 择 Debug window 命 令 , Watch window 子 命 令 。
这时变量窗口将出现在屏幕上。
图 4.17
在 图 4.17 的 观 察 窗 口 中 双 击 Name 栏 的 某 一 空 行 , 输 入 你 要 查 看 的
变量名或表达式。
图 4.18
回车后你将会看到对应的值。观察窗口可有多页,分别对应于标签
Watch1,Watch2,Watch3 等 等 。 假 如 你 输 入 的 表 达 式 是 一 个 结 构 或 是 一 个
对 象 ,你 可 以 用 鼠 标 点 取 表 达 式 右 边 的 形 如 + ,以 进 一 步 观 察 其 中 的 成
员 变 量 的 值 如 图 4.19。
27
图 4.19
♦ 快 速 查 看 变 量 对 话 框 (quick watch);
在快速查看变量对话框中你可以象利用观察窗口一样来查看变量或表达式的值。但我们还可
以利用它来该变运行过程中的变量,具体操作如下:
( 1) 在 Debug 菜 单 , 选 择 Quick Watch 命 令 , 这 时 屏 幕 上 将 会 出 现
Quick Watch 对 话 框 ;
图 4.20
( 2) 在 Expression 编 辑 框 中 输 入 变 量 名 , 按 回 车 ;
28
图 4.21
( 3 )在 C u r r e n t V a l u e 格 子 中 将 出 现 变 量 名 及 其 当 前 对 应 的 值 如 图 4 . 2 2 :
图 4.22
( 4) 如 要 改 变 该 变 量 的 值 只 需 双 击 该 变 量 对 应 的 Name 栏 , 输 入 你
要改变的值;
( 5) 如 要 把 该 变 量 加 入 到 观 察 窗 口 中 , 点 击 Add watch 按 钮 ;
( 6) 点 击 Close 按 钮 返 回 ;
♦ 我们还可以直接查看内存中的值
( 1) 从 View 菜 单 中 选 取 Debug windows 及 Memory 子 命 令 。 Memory
Window 出 现 ;
图 4.23
( 2) 在 Address 编 辑 框 中 输 入 你 要 查 看 的 内 存 地 址 , 回 车 。 对 应 内 存
地 址 中 的 值 将 显 示 在 Memory window 的 窗 口 中 。
29
图 4.24
♦ 在 调 试 过 程 中 ,有 时 我 们 需 要 查 看 或 改 寄 存 器 中 的 值 。我 们 只 需 :
( 1) 从 View 菜 单 中 选 取 Debug window 及 Registers 子 选 项 。
R e g i s t e r s 窗 口 出 现 。在 R e g i s t e r s 窗 口 中 ,信 息 以 R e g i s t e r = V a l u e
的 形 式 显 示 , 其 中 Register 代 表 寄 存 器 的 名 字 , Value 代 表 寄 存 器 中
的值。
图 4.25
( 2) 如 果 你 要 修 改 某 一 个 寄 存 器 的 值 , 用 TAB 键 , 或 鼠 标 将 光 标 移
到你想改变的值的右边,然后输入你想要的值。回车返回。
在寄存器中,有一类特殊的寄存器称为标志寄存器,其中有八个
标志位:
OV 是 溢 出 标 志 ;
UP 是 方 向 标 志 ;
EI 是 中 断 使 能 标 志 ;
Sign 是 符 号 标 志 ,
Zero 是 零 标 志 。
Parity 是 奇 偶 较 验 标 志 。
Carry 是 进 位 标 志 。
30
2 高级调试技术
前面我们讲了调试工具的使用,利用它可以就进行常规的调试,即
使程序在某处停下来,再观察程序的当前壮态。而且这些工具在且它调
试 器 中 也 有 。但 我 们 知 道 我 们 知 道 在 V C 程 序 的 开 发 过 程 中 ,光 有 这 些 工
具是不够的。为了更快更好地开发程序,我们还需要利用更高级的调试
工 具 。 我 们 知 道 , 在 利 用 VC 开 发 过 程 中 ,利 用 MFC 将 会 极 大 地 方 便 应 用
程 序 的 开 发 , 所 以 开 发 人 员 往 往 是 利 用 MFC 来 开 发 应 用 程 序 , 正 是 这 个
原 因 Microsoft 公 司 在 MFC 中 提 供 了 一 些 特 性 来 帮 助 你 进 行 程 序 的 调 试 。
我 们 知 道 在 MFC 中 , 绝 大 多 数 类 都 是 从 一 个 叫 做 Cobject 的 类 继 承
过来的,虽然这是一个虚基类,但它定义了许多成员函数,其中许多成
员 函 数 是 用 来 支 持 程 序 的 调 试 的 , 如 Dump ,Assertvalid 等 成 员 函 数 。
另 外 他 们 都 支 持 如 T R A C E , A S S E R T 等 宏 ,并 支 持 内 存 漏 洞 的 检 查 等 等 。我
们 知 道 ,为 了 支 持 调 试 ,类 库 肯 定 在 在 性 能 上 有 所 损 失 ,为 此 M i c r o s o f t
公 司 提 供 了 两 个 不 同 的 版 本 的 类 库 : Win32 Debug 版 本 和 Win32 Release
版本。在前面我们已经提到,每当我们建立一个工程时,我们也有对应
的 两 个 版 本 。 在 你 的 DEBUG 版 本 的 工 程 中 , 编 译 器 连 接 DEBUG 版 本 的
M F C 类 库 ;在 你 的 R E L E A S E 版 本 的 工 程 中 编 译 器 连 接 R E L E A S E 版 本 的 M F C
类库以获得尽可能快的速度。下面我们来介绍这些工具的利用。
2.1 TRACE 宏的利用
TRACE 宏 有 点 象 我 们 以 前 在 C 语 言 中 用 的 Printf 函 数 , 使 程 序 在
运行过程中输出一些调试信息,使我们能了解程序的一些状态。但有一
点 不 同 的 是 : TRACE 宏 只 有 在 调 试 状 态 下 才 有 所 输 出 , 而 以 前 用 的
Printf 函 数 在 任 何 情 况 下 都 有 输 出 。 和 Printf 函 数 一 样 , TRACE 函 数
可以接受多个参数如:
int x = 1;
int y = 16;
float z = 32.0;
TRACE( "This is a TRACE statement\n" );
TRACE( "The value of x is %d\n", x );
TRACE( "x = %d and y = %d\n", x, y );
TRACE( "x = %d and y = %x and z = %f\n", x, y, z );
要 注 意 的 是 TRACE 宏 只 对 Debug 版 本 的 工 程 产 生 作 用 , 在 Release
版 本 的 工 程 中 , TRACE 宏 将 被 忽 略 。
31
2.2 ASSERT 宏的利用
在 开 发 过 程 中 我 们 可 以 假 设 只 要 程 序 运 行 正 确 ,某 一 条 件 肯 定 成 立 。
如 不 成 立 ,那 么 我 们 可 以 断 言 程 序 肯 定 出 错 。在 这 种 情 况 下 我 们 可 以 利
用 ASSERT 来 设 定 断 言 。 ASSERT 宏 的 参 数 是 一 个 逻 辑 表 达 式 , 在 程 序 运
行过程中,若该逻辑表达式为真,则不会发生任何动作,若此表达式为
假,系统将弹出一个对话框警告你,并停止程序的执行。同时要求你作
出 选 择 : Abort, Ignore, Retry。 若 你 选 择 Abort, 系 统 将 停 止 程 序 的
执 行 ;若 你 选 择 Ignore 系 统 将 忽 略 该 错 误 , 并 继 续 执 行 程 序 ; 若 你 选 择
Retry , 系 统 将 重 新 计 算 该 表 达 式 , 并 激 活 调 试 器 。 同 TRACE 宏 一 样 ,
ASSERT 宏 只 DEBUG 版 本 中 起 作 用 , 在 RELEASE 版 本 中 ASSERT 宏 将 被 忽
略。
2.3 ASSERT_VALID 宏的利用以及类的 AssertValid()成员函的重
载
A S S E R T _ V A L I D 宏 用 来 在 运 行 时 检 查 一 个 对 象 的 内 部 合 法 性 ,比 如 说
现 在 有 一 个 学 生 对 象 ,我 们 知 道 每 个 学 生 的 年 龄 一 定 大 于 零 ,若 年 龄 小
于 零 , 则 该 学 生 对 象 肯 定 有 问 题 。 事 实 上 , ASSERT_VALID 宏 就 是 转 化 为
对 象 的 成 员 函 数 AssertValid()的 调 用 , 只 是 这 种 方 法 更 安 全 。 它 的 参
数 是 一 个 对 象 指 针 , 通 过 这 个 指 针 来 调 用 它 的 AssertValid()成 员 函 数 。
与 此 相 配 套 , 每 当 我 们 创 建 从 Cobject 类 继 承 而 来 的 一 个 新 的 类 时 ,
我们可以重载该成员函数,以执行特定的合法性检查。
2.4 对象的 DUMP 函数的利用
Dump 函 数 用 来 按 指 定 的 格 式 输 出 一 个 对 象 的 成 员 变 量 , 来 帮 助 你
诊 断 一 个 对 象 的 内 部 情 况 。 与 AssertValid 成 员 函 数 一 样 , Dump 也 是
Cobject 类 的 成 员 函 数 。 Dump 函 数 的 参 数 是 一 个 CdumpContext 对 象 ,
你 可 以 象 利 用 流 一 样 往 向 这 个 对 象 中 输 入 数 据 。 当 你 创 建 一 个 Cobject
继 承 而 来 的 新 类 时 , 你 可 以 按 如 下 步 骤 重 载 你 自 己 的 Dump 函 数 :
(1) 调 用 基 类 的 Dump 函 数 , 以 输 出 基 类 的 内 容 ;
(2) 向 Cdumpcontest 对 象 输 出 该 类 的 数 据 .
例 如 ,典 型 的 Dump 函 数 定 义 如 下 :
#ifdef _DEBUG
void CPerson::Dump( CDumpContext& dc ) const
{
// call base class function first
CObject::Dump( dc );
32
// now do the stuff for our specific class
dc << "last name: " << m_lastName << "\n"
<< "first name: " << m_firstName << "\n";
}
#endif
你 可 能 已 经 注 意 到 整 个 函 数 的 定 义 都 包 含 在 #ifdef _DEBUG 和
#endif 中 , 这 使 得 Dump 成 员 函 数 只 在 DEBUG 版 本 中 发 生 作 用 , 而 对
RELEASE 版 本 不 发 生 作 用 。
3 内存漏洞的检查
也 许 你 已 经 知 道 , 在 C++和 C 语 言 中 指 针 问 题 也 就 是 内 存 申 请 与 释 放
是一个令人头疼的事情,假如你申请了内存,但没有释放,并且你的程
序需要长时间地运行,那么,系统的资源将逐渐减少,当系统的资源全
部被用完时,系统将会崩溃。所以在开发程序的过程中一定要保证资源
的完全释放。下面我们来介绍内存漏洞的检查。
也 许 你 会 问 , 系 统 是 怎 样 支 持 内 存 漏 洞 的 检 查 的 ? 其 实 在 你 的 Debug
版 本 中 所 有 的 有 关 内 存 分 配 的 函 数 都 是 被 重 载 过 的 ,具 体 过 程 是 这 样 的 ,
当你的程序申请内存时,它首先调用一般的内存分配函数分配一块稍大
的 内 存 块 。 在 这 一 内 存 块 中 分 为 四 个 小 块 : Heap Information, buffer ,
User memory block, buffer。 第 一 块 为 有 关 堆 的 信 息 , 比 如 , 申 请 该 内
存 的 地 点 (文 件 名 , 行 号 ), 此 内 存 块 的 类 型 (如 整 型 , 浮 点 , 或 某 一 类 的
对 象 )等 等 。 第 二 块 是 一 个 缓 冲 区 ,用 于 截 获 用 户 对 其 申 请 内 存 使 用 越 界
的情况。第三块是真正给用户的内存,返回的指针也是指向这儿。第四
块也是一个缓冲区,作用同第二块。
当你申请的内存均被记录在案后,要检查内存漏洞就比较容易了,粗略地说,假如你要
检查某一程序段是否有内存漏洞,你只需在这一程序 段的开始要求系统为你做一个内存使
用情况的映象,记录下程序开始时的内存使用情况,然后在程序段的末尾再使系统为你做一
次内存映象,比较两次映象,以检查是否有没释放的内存,假如有未释放的内存,根据这一
块中有关分配情况的信息来告诉用户在那儿申请的内存没释放。
具体地讲检查内存漏洞需要以下几个步骤:
l 在你所检测的程序段的开始处建立一个 CmemoryState 对象,调用其成员函数
Checkpoint,以取得当前内存使用情况的快照;
l 在你所检测的程序段的末尾处再建立一个 CmemoryState 对象,调用其成员函数
Checkpoint ,以取得当前内存使用情况的快照;
l 再 建 立 第 三 个 CmemoryState 对 象 , 调 用 其 成 员函 数 Difference, 把 第 一 个
CmemoryState 对象和第二个 CmemeoryState 对象作为其参数.,如果两次内存快照
不相同,则该函数返回非零,说明此程序 段中有内存漏洞。下面我们来看一个
典型的例子:
// Declare the variables needed
#ifdef _DEBUG
33
CMemoryState oldMemState, newMemState, diffMemState;
OldMemState.Checkpoint();
#endif
// do your memory allocations and deallocations...
CString s = "This is a frame variable";
// the next object is a heap object
CPerson* p = new CPerson( "Smith", "Alan", "581_0215" );
#ifdef _DEBUG
newMemState.Checkpoint();
if( diffMemState.Difference( oldMemState, newMemState ) )
{
TRACE( "Memory leaked!\n" );
}
#endif
34
Download