注册 登录
编程论坛 C++教室

[连载]《易学C++》初稿完成,请发e-mail索取部分样稿

p1s 发布于 2005-12-10 15:06, 4399 次点击


为配合出版工作,暂停下载。如需要查看样稿,请发送e-mail到tomatostudio@126.com完整版含附录,其中包括C++常用关键字及含义、所有练习的参考答案等。

前 言

高级语言程序设计是各大院校计算机专业的一门专业基础必修课,主要是学习一些程序设计的基础知识和基本思路。学好高级语言程序设计对后继的一些数据结构、软件工程等课程有着重大的影响。而如果一名计算机专业的学生将来想在软件行业有所发展,也需要有着扎实的高级语言程序设计基础。因为高级语言是众多计算机语言中使用最多的。

然而,由于中学里的一些学习问题,各中学毕业的学生在计算机水平方面良莠不齐,有些甚至连一点点程序设计的基础也没有。到了大学里,老师们却因为课时紧张,难以把一些很基本的知识很详细地给学生们解释清楚,以至于日积月累之后,学生们还是觉得什么都不懂。于是,如何能够提高学生的专业水平,更快地把大家领进计算机的世界就显得尤为重要。

本书作者自幼接触并自学了BASIC语言,从初高中开始自学PASCAL语言和Visual Basic,对如何学习和掌握高级语言有一些自己的想法,在帮助一些没有程序设计基础的同学更快入门有一些自己的方法。于是就把这些想法和方法一一罗列出来,编著成书。

学习程序设计是一个循序渐进的漫长过程,在短短的几个学期内是不可能很好地掌握的。在学习过程中要求精求全实在是很有难度。而且
对初学者来说,知道得越多往往就越是迷茫。所以本书将其他教材中一些不常用的知识和原来的一些C语言的知识删去,添加了一些平时常用的算法介绍和一些可能和后继课程有部分关联的知识,以帮助大家更快地掌握高级语言程序设计的精髓。

本书作者也只是一名普通的大学生,在考虑一些非常专业的问题上可能尚有欠缺。而且由于时间较为仓促,书中难免有一些错误或不合理的地方,希望各位高人能够不吝赐教。如果您对本书有什么建议或者意见,请发送邮件到tomatostudio@126.com

使用指南

本书主要是依照计算机本科专业的实际教学需要来编排内容的。虽然内容的条理可能不及某些专业的C++工具书那么清晰,但是这样的次序让初学者比较容易上手。毕竟那些工具书是面向一些已经掌握C++或有较高的高级语言程序设计基础的读者。编写此书的主旨就是不要一下子把什么都说出来,而是一点一点循序渐进地增长读者的能力。这样,读者就不会一下子被那么多难以接受的概念吓住,以至于失去了继续学习的信心。

本书的主要论述对象是Microsoft Visual C++,对于以前的C语言和Borland的C++不作讨论,以免初学者把各种语言的概念混淆起来,也有效降低了学习的压力。对于一些VC++中存在却不常用的内容,本书一般一笔带过或不予提及。因为这些内容在应试方面不作要求,在实际使用上也可以由其他方法代替。但是,如果你是一位初学者,那么就请务必要看到本书的每一个角落。你所遗落的一句话就有可能是一个知识的关键点。

本书的内容有四个特点:

1、粗体字:读者必须掌握理解的内容,也是每个知识点的精髓或要点。很多初学者容易犯的错误也在粗体字中予以提醒。

2、试试看:把一些可能与一般情况不符甚至矛盾的情况列举出来,鼓励读者上机试验,以得到深刻的结论。这些结论可能对以后的学习有所帮助。所以建议所有有条件的读者务必去试试看。对于没有条件的读者,则需要牢记本书给出的结论。

3、算法时间:向大家介绍一些程序设计的常用算法。其实很多时候一个程序就是把这些算法以不同形式搭建起来。能够掌握这些算法不论是对阅读别人的代码还是自己设计程序都有着很大的帮助。

4、习题:帮助大家巩固已经学习的知识。有些题型则是符合应试的要求。从难度上来说,都算适中。如果读者已经掌握了章节中的知识,那么做这些习题也不会有什么困难。

本书的定位是C++程序设计的教学辅导书,而不是C++的工具书或语法书。如果你想要了解更多深层的内容,请查阅C++的专业工具书。

HTTP稳定下载地址:http://www.tomatostudio.com.cn/

QQ:149883760
E-mail:tomatostudio@126.com
作者的空间:http://spaces.msn.com/OOTTMA/

希望能够结交高手。

[此贴子已经被作者于2007-8-19 22:34:32编辑过]

48 回复
#2
unicorn2005-12-10 17:13
恩。。。支持支持
希望越多的人一起来讨论C++ 共同进步吧!
#3
yongke332005-12-10 21:52

我一定支持你

#4
ElfDN2005-12-10 23:23

钱能的那本教材对初学者就很好呀,自己看就可以看懂了

#5
p1s2005-12-19 11:14
内容节选
在日常生活中,我们经常会用到工具。比如我们画一个表格需要用到直尺,它可以告诉我们线段的长度;我们开瓶子的时候需要用开瓶器,它可以把瓶子打开;我们在做计算题的时候需要用到计算器,它能够告诉我们计算结果。
使用工具有些什么好处呢?首先,如果这个工具是现成的话,我们就可以不必要自己去做一个这样的工具,直接拿来就能用(比如开瓶器、计算器)。其次,不管是现成的工具,还是自己做的工具(比如自己做的直尺),一定是能够多次反复使用的(比如直尺不是用完一次就不能再用的),而且是能够在各种合适的情况下使用的。(直尺在量程范围内能量这条线段的长度,也能够量那条线段的长度。)
在程序设计中,我们也会有各种各样的“工具”。你告诉比较大小的“工具”两个不相等的数,这个“工具”能够告诉你哪个数大;你告诉求正弦值的“工具”一个弧度,这个工具能够求出这个弧度对应的正弦值等等……这些工具的名字就是函数(Function)。要注意,在程序设计中的函数和数学中的函数有相似的地方,但是它们却完全是两码事,请不要将两者等同起来。
函数和工具的性质是一样的。如果有一个现成求正弦值的函数,我们就不必自己去“造”一个这样的函数。求正弦值的函数是可以多次使用的,并且可以求出任意实数的正弦值(合适的情况下),但是它却求不出一个虚数的正弦值(不合适的情况下)。

在第一节,我们已经知道使用工具的好处,即可以重复使用和在各种适用情况下使用。函数和工具一样具有这些好处。但是除此以外,函数的存在还有着其他的意义。
一、现在要设计一个“学生信息处理程序”,需要完成四项工作,分别是记录学生的基本情况、学生成绩统计、优秀学生情况统计和信息输出。如果我们把四项工作全都写在主函数里面,那么我们就很难分清那一段代码在做什么。多层次的缩进和不能重复的变量名给我们阅读程序带来了困难。
如果我们为每一个功能编写一个函数,那么根据函数名每个函数的功能就很清晰了。如果我们要修改某一个函数的功能,其他的函数也丝毫不会受到影响。所以,函数的存在增强了程序的可读性。
二、需要设计一个规模很大的程序,它有几千项功能,把这些功能都编写在一个主函数里就只能由一个人来编写,因为每个人解决问题的思路是不同的,而且在主函数中的变量名是不能重复的,只有编写者自己知道哪些变量名是可以使用的。这样一来,没有一年半载,这个程序是无法完成的。
如果我们把这几千项功能分拆为一些函数,分给几百个人去编写,那么用不了几天时间这些函数就都能够完成了。最后用主函数把这些完成的函数组织一下,一个程序很快就完工了。所以,函数能够提高团队开发的效率。它就像把各个常用而不相关联的功能做成一块块“积木”。完成了函数的编写,编程就像搭积木一样方便了。


以上一些内容都是该书的节选。本书摒弃直接使用专业的术语,而是以形象的比喻来介绍程序设计中的内容,比如把变量比作箱子,把分支语句比作开关等等。对没有程序设计基础的同学来说,也能够轻松地看懂。另外,本书也注重提高读者的实际能力,在书中穿插讲述一些常用的小算法和小技巧,避免以前有些学生看得懂书却写不来程序的情况
如果你觉得上述资料对你学习“高级语言程序设计”这门课有一定的帮助,通过以下任一方式与我们联系,即可免费得到这些资料:
E-mail:tomatostudio@citiz.net
QQ:149883760
技术支持站点:http://www.firstdev.net/bbs/read.php?tid=114&u=41 (第一开发者论坛)

[此贴子已经被作者于2006-1-7 0:52:43编辑过]

#6
合金装备2005-12-21 18:03

强顶

#7
p1s2005-12-23 23:52

第五章内容简介

这章讲述的是循环结构。和钱能老师的教材不一样,我先来讲述for语句,并且把循环结构和赛车F1结合了起来,相信看过赛车的人应该都能看懂什么是循环语句了。其中还有一些编程小技巧,诸如控制变量和循环所要完成的工作的关系等等……希望大家多提宝贵意见!

[此贴子已经被作者于2005-12-23 23:52:48编辑过]

#8
p1s2006-01-07 00:37
第六章写好了,函数一直是初学者比较难以理解的一部分,不知道我这样写大家是不是能理解。如果觉得还是不太懂,欢迎大家提出来,我再想办法改进。还有如果哪位高手觉得我的理论或者语言有问题,也欢迎捉虫!地址在顶楼。
谢谢大家的支持!
#9
qsrock2006-01-09 13:47

厉害!~作为一个学生我和佩服楼主可以写出自己的书 !~
我现在的实力根本什么意见可提!~
等我看会了楼主的书再说好了!~
谢谢楼主的书哦!~
好象没有写完哦!

[此贴子已经被作者于2006-1-9 13:52:05编辑过]

#10
p1s2006-01-21 08:11

第七章节选

我们知道,在程序设计中,大多数数据都是存放在变量里的。如果我们要处理较多的数据,增加存放数据的空间最简单的方法就是多开设一些变量。然而,变量多了就难以管理了。这就好像一个班级里的学生名字有长有短,即使没有重复的名字,要在一长串名单里找到一个同学的名字也不是件容易的事情。于是,最方便的方法就是给同学们编上学号了,把名单按学号排列好以后,查找起来只要找学号就可以了。因为数字的排列是从小到大的,是有序的,所以查找起来要比在一堆长短不一的名字中查找要方便多了。
我们受到“学号”的启发,可以给变量也编一个号,把存储着相关内容的变量编在一组内,这就称为数组(Array)。

我们知道变量和数组都是放在内存里的,我们有时候还能够听到内存地址(Address)这个词。那么地址究竟是什么意思呢?
其实在内存里,就像是许许多多的街道,每条街道有它的名字,而街道上的每幢房子又按顺序地编了号,于是街道名和房子在街道上的编号就能确定内存中唯一的一幢房子,我们在这里认为所有的数据在内存中都是放在房子里。电脑就是依照这个原理找到所要的访问或修改的数据的。街道名和房子在街道上的编号就称为这个房子的地址。
那么这些内存中的房子和我们所说的变量和数组是什么关系呢?在内存里的房子的大小是规定的,每幢房子只能存储一个字节(Byte)的数据。(一个字节相当于一个半角的英文字母,一个汉字需要占用两个字节。)有时候,一种类型的变量需要比较大的空间,比如一个浮点型的实数,一幢房子是放不下的,而是需要4幢房子的空间才能放得下。于是电脑就把连起来的4幢房子拼起来,每幢房子放这个实数的一部分数据。而这连起来的4幢房子,构成了一个能够存放浮点型实数的变量。

什么时候要用二维数组呢?一般有两种情况,一种是描述一个二维的事物。比如用1表示墙,用0表示通路,我们可以用二维数组来描述一个迷宫地图;用1表示有通路,0表示没有通路,我们可以用二维数组来描述几个城市之间的交通情况。还有一种是描述多个具有多项属性的事物。比如有多个学生,每个学生有语文、数学和英语三门成绩,我们就可以用二维数组来描述。

[此贴子已经被作者于2006-1-21 8:11:38编辑过]

#11
dlcdavid2006-01-21 12:48
看了,,支持~~up~~~~
#12
ElfDN2006-01-21 12:54
恩,想对楼主说,希望知识还是能尽量的系统和高效来学,毕竟这个还是靠爱好才学得好的,就比如小生是学中文专业的,这个学的也不算太差
#13
welldone20062006-01-21 16:00
支持一下,期待可以早日看到
#14
p1s2006-01-21 18:14
以下是引用ElfDN在2006-1-21 12:54:00的发言:
恩,想对楼主说,希望知识还是能尽量的系统和高效来学,毕竟这个还是靠爱好才学得好的,就比如小生是学中文专业的,这个学的也不算太差

同意斑竹的观点,可是我身边就有一些人,不知怎么稀里糊涂地就来到了这个需要学习c++的专业。

#15
p1s2006-01-27 00:00

第八章讲述的是如何阅读代码。本来按照安排应该写指针和结构的,考虑到我校学生都放假,而且开学后不久就要考试,还是及早写一些有用的东西。等到阅读代码和调试写完以后,我会再回到前面写指针和结构。
在阅读代码中,主要介绍整体把握法、经验法和模拟法三种常用方法来读懂代码,对一些初学者来说,阅读了这一章可能对读代码有一些新的认识。
另考虑到pdf文件中的程序代码无法复制出来,而且手工输入有很麻烦,现在考虑过几天把代码文件都打包发上来,请需要的朋友记得关注一下。

[此贴子已经被作者于2006-1-27 0:00:16编辑过]

#16
frl5202006-01-30 09:58

你的东西对我这种一点基础也没有的初学者很有用啊 谢谢你 支持你 期待着更多的内容~!

#17
cgstar2006-02-04 19:19
多谢了~
#18
carter201002006-02-12 10:48

顶.......

#19
zys44162006-02-14 00:36
支持~~楼主上海大学~?我有个网友是上海大学商管系大一的,业余编程呢~~
#20
kai2006-02-14 08:01
p1s,
下载了你的书,匆匆看了一下。我非常认同你的想法,让每个人都能明白什么是编程的书在市面上几乎是没有。很多注明是入门类的书,搞得却像是复习类的书。
看了你的这个帖子之后,我也有了整理思想,然后写出来的冲动。大部分书都是作者提出观点,然后用DemoCode 来阐述他的观点。我想走另一种形式,那就是先提出问题,给出不同的解决方案,在这个基础上再点出观点。这样读者会有一个思考的过程,而不是填鸭似的灌输。比如我们为什么需要原始数据类型? 当你定义了 int a = 3; 的时候,对计算机来讲到底意味着什么? 我们为什么需要 array 也就是数组这个概念?进一步来讲 struct 到底意味着什么?在这样一层进一层的思考中,我希望让读者自己产生这样一个概念,那就是,我需要某种数据结构去表达我的数据,进一步读者便能理解我们为什么需要 class, 而这个正是OOP的一个关键所在。到这里可以说,刚刚踏入C++ 的大门。C++的大门内还有很多很多。
很高兴认识你,看得出,你是一个有所想法的人,以后多多交流。
#21
chinacuper2006-02-27 18:09

想不到在这里又能拜读到你的作品,真是让人兴奋。

还是那句话,希望你继续努力,为你的努力而喝采

#22
chinacuper2006-02-27 18:22

楼主能不能提个意见。

由于我不习惯在电脑上看书,所以总想把教材打印出来,可是现在的一些阅读器都设置了保护,你能不能提供WORD版的下载,方便我们打印出来后阅读,谢谢。

#23
p1s2006-03-31 23:05

对不起,好久没有更新了。不是我把写书忘了,也不是我偷懒了,前一段时间都在应付考试。好在最后成绩还算可以,也对得起我耽误下来的这些工作。

言归正传,这次我写的是指针。大家一直都说指针是最难学的部分,其实我也这样认为。我觉得这次有些地方还是写得有些不清不楚,所以还希望大家能够多多提意见。如果觉得哪里模糊了概念、难以理解或者不太合理,欢迎来和我交流。

在内容取舍方面,我把字符串和函数指针去掉了。因为C++主要是面向对象的,把C-String放进来讲得很深就有把C和C++风格混淆之嫌。所以我想等到面向对象了,把string和vector一起说。至于函数指针,基本上没什么大用处,大家还没学汇编,对指令什么的还不熟悉,所以不说也罢。

堆内存其实一直是指针里的重头戏,我把关于堆内存到底处在什么位置省略了,我觉得这个只会让初学者更迷茫。大家只要知道堆内存是动态申请的就可以了。要知道什么是堆内存,需要知道什么是堆什么是栈什么是段等等,对初学者来说没必要。对于C里面的malloc和free我也不说了,这个对初级C++程序员来说完全是增加负担,多此一举。

大家如果觉得有哪些重要的地方被我忽略了,希望能够提出来,我再斟酌一下,有必要的话我会补写。最后还是要感谢大家支持!下面给出本章一些节选:

什么是指针

在我们的桌面上,往往有这样一些图标:在它们的左下角有个小箭头,我们双击它,可以调出本机内的一些程序或文件。然而我们发现这些图标所占的存储空间很小,一般也就几百到几千字节。可是那么小的文件怎么会让上百兆的程序执行起来的呢?
后来,我们知道那些有小箭头的图标文件称为快捷方式。它所存储的内容并不是所要调用的程序本身,而是所要调用的程序在本机磁盘上的位置。(比如D:\Tencent\QQ\QQ.exe,如图8.1所示)使用快捷方式的目的就是为了快捷方便,不用去查找程序就能去执行它。不过如果所要调用的程序不存在或位置不正确,那么双击了这个快捷方式就会导致错误发生。
在内存中,可能会有一些数据,我们不知道它的变量名,却知道它在内存中的存储位置,即地址。我们能否利用快捷方式的原理对这些数据进行调用和处理呢?
很幸运,在C++中,也可以给内存中的数据创建“快捷方式”,我们称它为指针(Pointer)。它和整型、字符型、浮点型一样,是一种数据类型。指针中存储的并不是所要调用的数据本身,而是所要调用的数据在内存中的地址。我们可以通过对指针的操作来实现对数据的调用和操作。

更灵活的存储——堆内存

家里要来客人了,我们要客人们泡茶。如果规定只能在确定来几位客人之前就把茶泡好,这就会显得很尴尬:茶泡多了会造成浪费,泡少了怕怠慢了客人。所以,最好的方法就是等客人来了再泡茶,来几位客人泡几杯茶。
然而,我们在使用数组的时候也会面临这种尴尬:数组必须在程序运行前声明,即数组的大小在编译前必须是已知的常量表达式。空间申请得太大会造成浪费,空间申请得太小会造成数据溢出而使得程序异常。所以,为了解决这个问题,我们需要能够在程序运行时根据实际情况申请内存空间。
在C++中,允许我们在程序运行时根据自己的需要申请一定的内存空间,我们把它称为堆内存(Heap)空间。

#24
heliujin2006-04-01 08:57
什么也不说了 必须顶一下了


#25
p1s2006-04-22 11:11
这几天又收到不少来信,QQ的好友数也增加了不少。甚至有一位高龄网友说想按正式出版计价,寄一些钱给我。在此,我感谢各位对我长期以来的支持和鼓励!也感谢各位细心地阅读了我的作品,给我提出了一些非常好的建议。

这次更新花了三周的时间。本来结构这个东西就包含了链表这些内容,我原认为链表应该交给数据结构的,不过考虑到学校里还是要求这个的,我就说一下吧,这次选用自行车链条作为比喻,图画得比较难看,请多包涵。枚举本来在学校里一带而过的,不过发现Java也都开始支持枚举了,可见这个类型还是蛮有用的。这次我花了一个小节来说枚举。两周半就用在了枚举、结构、链表上了。还有半周呢我对前面作了一些修改,在第一章添加了程序设计到底要做什么,在第三章添加了常用数据类型的简介。如果大家觉得哪里还写得欠妥,请给我建议,谢谢!
本次内容节选:

很多初学者会不解:程序设计到底是要做什么呢?我们该如何教会电脑解决问题呢?
其实,要解决一些看似不同的问题,我们可以将其归结为一种确定的过程和方法。我们把这种能够解决一类问题的过程和方法称为算法(Algorithm)。下面,我们以解一元二次方程为例,介绍求解的算法:
(1)输入二次项系数a,一次项系数b和常数项c。
(2)计算△=b2-4ac。
(3)判断△的大小,如果△≥0,则有实数解,否则就没有实数解。
(4)如果有实数解,就利用求根公式求出两个解。
(5)输出方程的两个实数解,或告知无解。
以上便是用自然语言描述的求解一元二次方程的算法。程序设计所要做的便是探求这种能解决一类问题的算法,并且要将这种算法用计算机能够“看懂”的语言表达出来。

在基本的数据类型中,无外乎就是些数字和字符。但是某些事物是较难用数字和字符来准确地表示的。比如一周有七天,分别是Sunday、Monday、Tuesday、Wednesday、Thursday、Friday和Saturday。如果我们用整数0、1、2、3、4、5、6来表示这七天,那么多下来的那些整数该怎么办?而且这样的设置很容易让数据出错,即取值超出范围。我们能否自创一个数据类型,而数据的取值范围就是这七天呢?
C++中有一种数据类型称为枚举(Enumeration)类型,它允许用户自己来定义一种数据类型,并且列出该数据类型的取值范围。

我们把变量比作为箱子。在现实生活中,如果小箱子太多太杂乱了,我们会拿一个大收纳箱来,把小箱子一个个有序地放到收纳箱里面。这样一来,在我们视线里的箱子就变少了,整理起来也会比较方便。那么,我们能否把这么多凌乱的变量整理到一个变量当中呢?
C++中有一种数据类型称为结构(Structure)类型,它允许用户自己定义一种数据类型,并且把描述该类型的各种数据类型一一整合到其中。

相信很多人都玩过即时战略游戏(RTS),比如时下流行的魔兽争霸、曾红极一时的红色警戒。可是大家有没有考虑过,每个战斗单位都有它们各自的属性,电脑又是如何为我们造出来的部队分配内存的呢?
显然,部队的数量在程序执行之前是未知的。如果用数组来存储这些数据,那么就会造成游戏前期浪费内存(没有那么多的部队),游戏后期存储空间不够(战斗单位数大大增加)的情况。
那么使用数组的动态分配行不行呢?还是不行。因为部队的数量在程序执行的时候仍然是未知的。甚至连玩家自己也不知道要造多少战斗单位,只是根据战斗的实际情况来发展自己的势力。所以,这时候最合理的内存分配方式就是每造一个战斗单位分配一个内存空间。
然而,新问题又出现了:建造各单位的时间一般不可能是完全连续的,根据不同时刻程序运行的实际情况,每个单位分配到的内存空间也不是连续的了。空间不连续就意味着没有了方便的数组下标。我们就很难把这些零零散散的内存空间集中起来管理。
链表的出现改变了这个情况。它可以在程序运行时根据实际需要一个个分配堆内存空间,并且用它的指针可以把一系列的空间串联起来,就像一条链子一样。这样一来,我们就能够利用指针对整个链表进行管理了。
#26
zk5162006-04-25 10:33
拜托  给我讲讲C图形编程吧
现在整天在黑屏下晃
实在不舒服!!
求你了!!
我现在连在VC++6.0上实现一个矩形框都不会!!!
#27
p1s2006-07-02 13:32

终于把第十一章写完了。感觉不太好,还是觉得很难把握“讲多少”的尺度。说多了读者看不懂,说少了读者还是看不懂。如果大家对这一章觉得有难以理解的欢迎来和我联系。

这一章主要讲了静态、全局变量,变量的作用域和可见性,头文件和程序的调试。前面一部分内容主要是对以前一些知识的补充。而程序的调试我自认为还是有一些创意的,介绍了如何快速地找到语法错误,如何观察程序的运行和Debug的使用。至少我在别的书上很少看到介绍这方面的知识。建议一些看得懂程序却自己编不好程序的初学者来看看。

另外书本改名了,原因是大家都说这名字太土。封面也重新做了,原因是大家都说原来的难看。希望这次修改没有让它们变得更差……

感谢室友qqqqaug当时想出来的英文名给了我灵感。感谢我的高中同学对我的支持!

本次内容节选:

我们已经说明,变量可以使用的范围是由变量的作用域决定。不同层次的变量的作用域,就好像大小不一的纸片。把它们堆叠起来,就会发生部分纸片被遮盖的情况。我们把这种变量作用域的遮盖情况称为变量的可见性(Visibility)。然而,当两张纸处于同一个层次,显然两者就不可能发生遮盖了。所以,如果我们在同一个层次中声明两个名字相同的变量,那么他们的作用域就不是遮盖,而是冲突了。因此,在某个函数的同一语法层次内不能声明多个名字相同的变量。

#include是一条编译预处理命令。什么叫编译预处理命令呢?我们知道,程序中的每一句语句会在运行的时候能得到体现。比如变量或函数的声明会创建一个变量或者函数,输出语句会在屏幕上输出字符。然而编译预处理命令却不会在运行时体现出来,因为它是写给编译器的信息,而不是程序中需要执行的语句。编译预处理命令不仅仅只有#include一条,在C++中,所有以#开头的命令都是编译预处理命令,比如#if、#else、#endif、#ifdef、#ifndef、#undef和#define等等。

调试主要分四个步骤和两种处理方式。我们把程序的编译和连接统称为编译阶段,把程序的运行和测试统称为运行阶段。在编译阶段发生的错误称为编译错误(Compile Error),在运行阶段发生的错误称为运行时错误(Runtime Error)。对于编译错误,我们通过检查并修正语法错误来解决;对于运行时错误,我们通过检查并修正语意(程序设计思想)错误来解决。

在介绍如何使用Debug工具之前,我们要介绍一下什么是断点(Breakpoint)。当程序运行到断点的时候,它会暂时停止运行后面的语句,供用户观察程序的运行情况,并等待用户发出指令。断点不是语句,而是附在某条语句上的一个标志。

#28
frl5202006-07-20 21:48

还是支持你````

#29
p1s2006-08-15 22:09

这次更新的周期实在是有点长了,主要是最近真的很忙,一边上课做单片机实验,一边ACM训练,一边帮论坛做手册,一边还在写着自己的书。
通过上次和一位学长的交流,我觉得还是有必要把主函数返回类型改为整型。的确,在有标准存在的情况下,不符合标准的教材就是不合格的产品,所以,无论如何应该贯彻这个标准。于是我把所有的主函数全都改为返回整型了。如有疏漏或写的不自然之处,欢迎来提建议!
本册更新的主要内容是如何编写程序,我从算、找、实现功能三方面作为分类,讲述如何解决问题,编写程序,并在“算法时间”中,指出了掌握好这三者的核心。接下来稍微介绍了一下驱动测试的程序设计,其实这也是偶然的机会在编译原理课上和别的同学讨论看到的,我觉得这种主导思想和我自己的想法很符合,而且很多时候我也是用类似与这样的方法来做一个程序的,所以也拿出来提一下。最后讲了递归,其实我自己并不爱好递归,平时也不经常使用,虽然也用来写过程序。我想主要是要交待一个调用函数的机制,这对了解高级语言和对以后数据结构的学习有好处。
本次内容节选:

当我们遇到一个问题,它往往不是一个直接用程序代码描述的问题,比如:统计销售量和利润,寻找出行的公交线路,将中文翻译成英语等等。所以我们先要把实际问题转化成一个电脑能够解决的问题,而大多数问题一般分为三类:
(1)算:计算利润,计算一元二次方程的根,计算一个数列的和等等。
(2)找:找最大的一个数,找最短的一条路径,找一个字符串的位置等等。
(3)实现功能:实现撤销、重做的功能,实现模拟某种操作的功能等等。有时候实现功能问题可以拆解为若干个“算”和“找”的问题。

大家以前可能有过这样的经历:如果做一份练习卷,是做一道题目核对一次答案的正确率高,还是做完所有题目再核对答案的正确率高?一般来说是前者。造成这种结果的原因有二:如果所做的每道题都是正确的,那么无疑给解答后面的题目增加了信心;如果某道题做错了,那么立刻就能发现问题,并且保证在之后不会再犯同类的错误。
我们开发程序,就如同是做一份练习卷。传统的开发方式是等到整个程序完成后再进行测试,这给开发者的心理带来阴影,也给查找问题的症结带来了麻烦。而如果每做完一个功能模块就测试一次,则可以在小范围内找到问题所在。在积累经验的同时也给自己增加了信心。
以上只是测试驱动程序开发的一种主导思想,在我们编写程序的时候值得借鉴采纳。

可通过递归解决的问题往往能够转化为若干个解决步骤,并且第n步和第n+1步有着类似或相同的限制条件。比如一个数列的递推公式:
an=3an-1+2,an-1=3an-2+2,an-2=3an-3+2,……,a1=3a0+2,a0=1,它的相邻两项有着相同的关系,即求第n项和第n+1项有着相同的方法。唯一的不同就是a0=1,我们把这种与众不同的方法称为递归出口,不断调用函数的递归将在那里终止。

#30
heliujin2006-08-15 22:40
我什么也不说了
顶吧~~~~~~~~~~~~~~~~
#31
p1s2006-08-31 21:15

  这次写的是一个短章节。终于开始面向对象啦!其实我觉得我认识的对象还是从VB开始的。一上来就说什么是对象,越说越不清楚。但是一旦先学会了使用对象,那么在使用中就会慢慢体会到什么是对象了。所以本章主要让读者学会使用字符串和向量。只要大家能够看懂程序,会自己改写就行了,不必去深究一些概念。下一章会从概念上更加详细地阐述什么是对象,什么是类。
  字符串和向量的使用中有出现迭代器的概念,虽然它是个非常重要而常用的东西,但是为了不增加大家的负担,我还是避开了这方面的知识。我会根据本章发布后的反馈情况来决定是否以后再补充这个知识点。

本次内容节选:

  其实单词Object更直观的翻译应该是物体。世界就是由各种物体组成的,比如某一辆汽车、某一个人、某一个杯子等等,这些都可以看作对象。
  任何一个对象往往有一些具体的属性,比如某汽车的品牌、型号、排量,某人的性别、身高、体重,某杯子的口径,材质等等。任何一个对象往往能进行一些操作,比如汽车可以开动、拐弯,人可以走路、吃饭,杯子可以被打破等等。
  所以,对象就是任何我们可以想象出来的具体的物体。

  在使用字符串类的时候,我们发现它和字符数组一个很明显的不同就是,我们无法对数据进行直接的修改和操作。如果有一个char a[]="Hello";,那么我们可以直接用a[0]= 'h';来修改存储在内存中的字符,甚至我们可以输出数组的首地址来了解这个数组到底存放在什么位置。而对于一个string a("Hello");,我们却无法直接修改它的数据,因为所有对a的操作都是由成员函数所定义的。我们只能了解这个字符串的存在,但它具体存储在于内存的什么位置,我们无法通过除了对应操作以外的简单方法得知。(如使用取地址操作符)
  由于我们不是字符串类的设计者,当我们对string进行种种操作时,我们只能了解到它的操作结果,而对它的操作原理和操作实现过程却无法得知。
  我们把类的数据不可知性和操作实现过程不可知性称为类的封装性。
  不难理解,作为使用者,我们不需要对数据和操作实现过程感兴趣。就好像买一个手机,我们只关心它是否能够正常通话,正常发短消息,却对它如何接通电话,如何把信号发送出去等等不感兴趣。类的封装性把类的设计者和类的使用者分隔开,使他们在设计程序时互不干扰,责任明确。

  在编写链表程序的时候,我们一定有这样的困惑:链表里面存储的数据类型可能是各种各样的,难道我们要为各种数据类型都写一个链表程序么?我们能不能写一个万用的链表程序呢?
  在PowerPoint之类的软件中,有一种模板功能。模板提供的文档框架是基本完整的,我们只需要在一些地方填写上自己需要的内容,就是一个完整的文档。在C++中,也有这么一种模板(Template),我们只需要在使用之前填写自己需要的数据类型,就是一个完整的程序。我们把具有模板功能的类称为模板类,向量就是一个模板类。在这一节,我们只需要了解如何使用模板类。关于更多模板的知识,将在后面的章节再作介绍。

#32
p1s2006-11-27 08:47

这一章竟然写了三个月,看来效率有点低下了。好在自己把一些主要的问题都讲清楚了,至于要想写得更好,还是以后再不断改进吧。我在面向对象的开头写了两章短篇,就是为了将最简单的问题讲清楚,而不给读者产生压力。试想如果学习一个完全不懂的概念,前两章就有半本书那么厚,读者一定会以为这个内容很难了。另外最近在高中里教书的时候发现一些初学者常见的问题,于是对前面的章节有所修改和加强。可见,实践是很重要的。
接下来写的思路是这样,面向对象的基本知识主要是围绕一个链表类的程序来说,比如什么构造函数,析构函数,静态成员数据,类模版等等;继承,多态,友元主要围绕一个文字RPG游戏来说。

本次内容节选:

我们已经了解了数据类型和变量的关系。数据类型是各个变量的归类,而变量则是某个数据类型的具体表现,我们称变量为数据类型的一个实例(Instance)。各个变量都有他们的属性:内容和占用内存空间等等;各个变量都有他们的操作:算术运算或逻辑运算等等。从这个角度来看,类和对象的关系就如同数据类型和变量的关系。我们不妨将类看作一种自定义的数据类型,那么对象就是一种属于该类型的变量。

所谓公有,就是外部可以访问的数据或执行的操作。比如一个人的身高(数据)是可以较直接地获得的,一个人吃东西(操作)是可以受外部控制的。私有就是外部不能直接访问的数据或执行的操作。比如一个人的心跳次数(数据)和消化过程(操作),虽然他们都是客观存在,但我们却不能直接地获取心跳数据或控制消化过程。
如果一个类的所有数据和操作都是公有的,那么它将完全暴露在外,同结构一样没有安全性。如果一个类的所有数据和操作都是私有的,那么它将完全与外界隔绝,这样的类也没有存在的意义。

由于数据封装在类的内部,在处理一些问题的时候就需要小心翼翼,不能把成员数据破坏了。以前我们介绍使用const来保护变量(就变成了常量),或保护指针所指向的内容,那么在类中,我们也可以使用const这个关键字来保护成员数据不被成员函数改变。我们把这种成员函数称为常成员函数。

#33
p1s2007-02-10 16:04

这一章主要是讲构造函数和析构函数。因为我在学的时候就认为拷贝构造函数就是构造函数的一种重载,所以把它也拉到这个章节里面来了。至于什么无名对象,至少在大学阶段用的机会不会很多,所以干脆就不说了。这段时间又有好多老师、出版社和我联系出版事宜,我想等到下一章写完,差不多也就剩2-3章左右,到那时候可以好好考虑一下出版的问题。

本次内容节选:

既然init函数担负着初始化对象的重任,那么它就必须和对象的声明“出双入对”了。万一忘记对对象进行初始化,程序就可能会出错。这就像在病毒肆虐的今天,保证电脑安全的病毒防火墙必须在开机之后立刻运行一样。万一哪天开了电脑忘记运行病毒防火墙,那么后果可能很严重。不过,你使用的病毒防火墙是你每次开机以后自己去点击运行的么?那样岂不是很麻烦?你是否知道,我们使用的病毒防火墙往往是随系统一起启动的?这给了我们一些启示:有的程序能够随着系统的启动而自动运行,那么会不会有一种函数,随着对象的创建而自动被调用呢?有!那就是构造函数(Constructor)。

根据程序的运行结果,我们发现头结点的构造函数比链表的构造函数优先运行。这也不难理解:构造函数的目的是要初始化成员数据,初始化成员数据的时候这个成员数据是必须存在的。所以当一个成员数据是一个对象的时候,应当先产生这个子对象,于是就先调用了子对象的构造函数。

我们看到,现在即使运行a.Destroy()之后,链表b里面的数据仍然能够正常显示。这是因为深拷贝构造函数是真正意义上的复制了链表a,并且使得链表a和链表b各自独立,互不干扰。这才是自定义拷贝构造函数存在的重要意义。

在学习链表的时候,我们知道结点是动态生成的,如果在程序结束之前不释放内存,就会造成内存泄漏。虽然我们已经编写了成员函数Destroy来删除所有动态生成的结点,但是如果我们不知道这个链表对象何时不再使用,那么调用Destroy的时机对我们来说就是个麻烦了。如果过早地调用,则后面的程序可能会出错。既然有构造函数能随着对象的创建而自动被调用,那么有没有一种函数能随着对象的消亡而自动被调用呢?有!那就是析构函数(Destructor)。

#34
p1s2007-03-09 21:08

这一章是面向对象初级部分的大杂烩,什么静态成员、友元、操作符重载都在。在钱能的书里面,操作符重载独立成章的。但是我觉得操作符重载和友元隔开的话,友元容易被忘记。至于输入输出流是否独立成章,这个到时候再看。下一章将介绍继承和多态,主要围绕一个文字RPG游戏。相信这个游戏能比较好地解释类与类之间的继承关系,也能引起大家的学习兴趣。为了配合出版工作,下一章将会是网上发布的最后一章。如果某些读者有特殊的需要,可以和我联系索取之后章节的电子版。

本次内容节选:

该程序中出现了两种调用静态成员函数的方法,一种是类名::静态成员函数名(参数表),另一种是对象名.静态成员函数名(参数表),这两种调用方法的效果是相同的。由于静态成员函数是属于类的,不是属于某一个具体对象,所以它分不清到底是访问哪个对象的非静态成员数据,故而不能访问非静态成员数据。

类似于链表类和链表结点类的问题,我们可以用友元类来解决。即链表类是链表结点类的“朋友”,可以直接访问链表结点类的私有成员数据或私有成员函数。显然,要做链表结点类的“朋友”,必须要得到链表结点类的认可。所以我们必须在链表结点类的声明中告诉电脑,链表类是它认可的“朋友”,可以访问它的私有成员。

在声明和定义操作符重载时需要注意以下几点:
(1)操作符只能是C++中存在的一些操作符,自己编造的操作符是不能参与操作符重载的。另外,“::”(域解析操作符)、“.”(成员操作符)、“……?……:……”(条件操作符)和sizeof等操作符不允许重载。
(2)参数表中罗列的是操作符的各个操作数。重载后操作数的个数应该与原来相同。不过如果操作符作为成员函数,则调用者本身是一个操作数,故而参数表中会减少一个操作数。
(3)各个操作数至少要有一个是自定义类型的数据,如结构或类。
(4)尽量不要混乱操作符的含义。如果把加号用在减法上,会使程序的可读性大大下降。

前增量操作符是“先增后赋”,在操作符重载中我们理解为先做自增,然后把操作数本身返回。后增量操作符是“先赋后增”,在这里我们理解为先把操作数的值返回,然后操作数自增。所以,前增量操作返回的是操作数本身,而后增量操作返回的只是一个临时的值。

#35
p1s2007-07-06 10:54

终于将第17章写完了。这一章是整本书中内容最多的一章。虽然C++在继承这块的内容很多,但是我还是做了删减。比如多重继承这种常常被人批判的内容,我只简要介绍了一下概念。对于本科生来说,这些内容应该已经够用。特别是在企业面试的时候,常常是问一些很基础的概念。面向对象的精髓并不是靠看几本书就能了解的,而是要在实际的工程中才能领悟的。

看到众多网友来信与我交流,肯定我的写作风格、向我提出众多宝贵的建议,我感到非常高兴。本章将会是我公布在网上的最后一个章节。之后的2-3章或许大家以后可以在书店里看到。近期我会和各大出版社联系,讨论出版事宜,以便更多的读者能够快速踏入C++程序设计的大门。如果您有在出版社工作的朋友,并且看好我的作品,也欢迎您主动来和我联系!我的邮箱地址为tomatostudio@126.com

本次内容节选:

如果有一个类,我们可以将其实例化,成为若干个对象。另外,如果我们希望对这个类加以升级改造,我们可以将这个类继承,形成子类(或者称为派生类),被继承的类则称为父类(或者称为基类)。实例化和继承是一个类的两种发展方向。继承能够减少我们开发程序的工作量,提高类的重用性。

如果我们把编写一个类看作是一次生产,那么产品(即编写出来的类)可以有两种用途:一种是将产品直接使用,相当于将类实例化;另一种是将产品用于再生产,相当于将类继承。类在这种不断的“再生产”中变得更为强大、健全。

private是私有继承,或称为私有的实现继承。它主要体现的是父类成员的重用。父类所有的公有、保护成员继承到子类时,类型会发生改变。父类的公有成员在子类中变成了私有成员,父类的保护成员在子类中也变成了私有成员。这时,我们可以利用从父类继承而来的成员函数来实现子类的成员函数,并且不必担心外部直接访问父类的成员函数,破坏了子类的秩序。比如我们认为栈是一种特殊的链表,它只能从链表尾部添加或删除结点,栈的压栈和退栈功能可以方便地由链表类的成员函数实现。但是,如果外部还能直接访问从链表类继承而来的成员函数,那么就可以在栈的任何位置插入结点,栈就会被破坏。

在使用继承的时候,子类必然是在父类的基础上有所改变。如果两者完全相同,这样的继承就失去了意义。同时,不同子类之间具体实现也是有所区别的,否则就出现了一个多余的类。不同的类的同名成员函数有着不同的表现形式,称为多态性。多态性是符合人的认知规律的,即称呼相同,所指不同。比如,学生类及其子类都有学习这个成员函数,但本科生、中学生、小学生的学习内容并不相同;玩家类的子类都有攻击这项技能,但剑士、弓箭手和魔法师的攻击方法不同。

多态是在程序员没有指定调用父类还是某个子类的成员函数时,电脑根据程序员的要求,揣测并选择最合适的成员函数去执行。但是当成员函数的参数格式不同时,程序员在调用成员函数的各种参数无疑就是在暗示到底调用哪个成员函数。这时电脑岂敢自作主张揣测人类的心思?因此,要使用虚函数实现多态性,至少要使各个函数的参数格式也完全相同。

C++中的确能实现多继承,但是在某些问题上并不是处理得很好。比如车和船同时具有长、宽、高等属性,要搞清水陆两用车的长、宽、高到底是从哪个类继承来的,着实要花费一些功夫。应该说,C++中多重继承的思想是优秀的,但是它的实现却是混乱的。有不少人都认为多重继承是C++的一个败笔,它把原本简单的单一继承复杂化了,使程序员很难再把思路理清。所以,即使是经验丰富的程序员,在大多数情况下也不会去使用多重继承。

#36
aipb20072007-07-06 11:53
佩服楼主,连载了两年。

赞!!!
#37
孤魂居士2007-07-06 15:30
#38
p1s2007-07-28 23:14

最近更新节选:

cout和cin并不是语句,而是标准输入输出流的类对象。它们分别被定义在istream.h和ostream.h头文件中。那么什么是流呢?简单地说,数据如同流水线上的物品在电脑中传输。要读取流中的数据(把输入流中的数据读到内存中),就如同取下流水线上的物品,这就是抽取;要向流中写入数据(把数据放到输出流中输出),就如同往流水线上放东西,这就是插入。

cerr和cout类似,也是输出流对象。当然,它们也有区别。cout是平时常用的普通输出,可以被重定向。而cerr正如其名,通常是异常情况下输出重要的出错信息。它只会输出到屏幕上,无法被重定向。这样,用户才不会因为信息被重定向而错过了重要的出错提示信息。

为什么不能像标准输入输出流一样有个现成的fin或者fout对象呢?这也不难理解:文件输入输出流的输入输出设备是磁盘文件,但是磁盘上有那么多文件,到底应该使用哪一个呢?所以,C++干脆就把创建对象的任务交给用户了。我们可以在创建对象自动调用构造函数时,设定输入或输出到哪个文件。

我们用形如cin >>a;的方式输入数据,就像是按“个”为单位取下流水线上的物品。因为侧重点在于单个物品,空格就犹如分隔物品的纸箱格,与其他数据一起被丢弃。我们也可以使用cin的成员函数getline来获取数据,这时候就像按“箱”为单位取下流水线上的物品。纸箱格作为取下的东西的一部分,不会被丢弃。

我们已经知道,插入操作符应该有两个操作数。前者是ostream类的对象(引用),后者是需要输出的内容,两者的次序不应该颠倒。如果把插入操作符作为成员函数,那么就会影响了操作数的次序。因此,通常情况下,我们将插入操作符作为友元函数。

#39
野比2007-07-29 09:10
不错..只是...可不可以在前面加个目录啥的呢? 那就更好了..
#40
p1s2007-08-16 15:23

最近更新节选:

通常情况下,我们把实现某一个独立功能的模块编写为一个函数。在函数的参数列表中,需要说明各个参数分别是属于什么数据类型的。然而,有的时候函数的功能与参数的数据类型是完全无关的。最典型的功能就是变量数据的交换:不论变量a和变量b的数据类型是什么,只要它们两者数据类型相同,并且能交换,这个函数就完成了它的使命。

除了函数可能与参数的数据类型无关之外,类也可能与之服务的数据类型无关。比较典型的就是用于存储数据的类。数据的类型众多,为了让类能够存储各种各样的数据,不见得给每个数据类型都定义一个类吧?这时后,我们想到了模板。当我们要使用一个类时,可以根据需要在类模板中填写合适的数据类型,生成一个模板类。

从原理上来说,我们先给类模板填上数据类型,使其成为一个模板类。如Linklist<int>和Linklist<char>都是模板类,它们是类模板的一个类实例。然后,我们可以创建模板类对象。如链表a和b,他们都是模板类的对象实例。所以,从类模板到对象应该经过两个实例化的过程。

#41
p1s2007-08-16 15:24
以下是引用野比在2007-7-29 9:10:47的发言:
不错..只是...可不可以在前面加个目录啥的呢? 那就更好了..

书的前面有目录的

#42
aipb20072007-08-16 16:05
非常不错,下载了看一下!
多多交流~~~~~~
#43
catseal2007-08-17 18:04

真的很喜欢这种看起简单点的资料,希望楼主加油,早点完成.

#44
heliujin2007-08-17 18:24
blueworld410@yahoo.com.cn
把最后几章的都发过来吧,真的很喜欢你的书,你是个伟大的人啊。
#45
孤独的男孩2007-08-18 11:48
楼主我也来看看你伟大的著作。yangxueliang2@163.com
#46
jianfeng112007-08-18 22:19
谢谢了,我要
邮箱:gonglong080@163.com
#47
圆圆的鸟蛋2007-08-18 22:34
楼住强啊!不过我怎么下不了呢??
#48
哲哲2007-08-20 16:44
发一份给我吧,zhezhe1980@hotmail.com
#49
cinki2007-08-21 13:38
LZ给的地址..我怎么不能下载啊 可以MAIL给我吗? 谢谢 LYLTZ@HOTMAIL.COM
1