2008年11月30日星期日

C++良好编程习惯(一)

--------摘自《C++编程金典》

  1. C++程序应以简单和直接的方式编写。这有时也称为KIS(“keep it simple”的简称,即“尽量简单”)编程方法。千万不要去尝试一些古怪的用法,去“折磨”这种语言。
  2. 仔细阅读你所用的C++版本的用户手册。经常查阅这些手册,才能确保自己能了解并正确使用C++的丰富特性。
  3. 计算机和编译器是最好的老师。仔细阅读了C++语言手册之后,如果你不能确定一项C++的特性是如何工作的,不妨用一个短小的“测试程序”进行实验,看其效果。设置编译器选项,令其报告“最多的警告”。研究程序编译时出现的每一条错误消息,并对程序进行纠正,去除这些消息。
  4. 每个程序都应以注释开头,以描述该程序的用途。
  5. 许多程序员让函数打印的最后一个字符是换行符(\n)。这样可保证函数将屏幕光标定位在一个新行的起始处。这样一个自发的约定可促进软件的重用能力——此为软件开发环境的一个重要目标。
  6. 针对每个函数的主体,令其在定义函数主体的花括号内部,缩进一个级别的位置。这样可使程序的函数结构更清晰,增强其可读性。
  7. 先为你喜欢的缩进距离拟出一个约定,然后始终坚持这一约定。可考虑用制表位(按Tab键)生成缩进。但是,不同的系统其制表位的距离往往不同。因此,建议你要么使用1/4英寸制表位,要么(一种更好的做法)用三个空格构成一个缩进级别。
  8. 有的程序员喜欢行行声明一个变量。采用这种格式,可方便地在每个声明后插入说明性的注释内容。
  9. 每个逗号(,)后面都应插入一个空格,以增强程序的可读性。
  10. 挑选一个有意义的变量名,将有助于保障程序的“自编档能力”;也就是说,只需读一读程序,即可轻松理解它,而不是必须求助于手册,或使用过多的注释。
  11. 避免标识符以下划线和双下划线开头,因为C++编译器可能采用这种形式的名称为其内部的某些用途提供服务。这样,有助于避免你选择的名称同编译器选择的名称混淆。
  12. 可执行语句之间的声明之前,需插入一个空行。这样可在程序中突出声明语句,使程序更加清晰。
  13. 如果你喜欢在一个函数的起始处放置声明,请用一个空行,以区分声明与函数中的可执行语句,突出声明结束的位置和可执行语句的开始位置。
  14. 在二元操作符的两端,请分别添加一个空格。这样可突出显示操作符,增强程序可读性。
  15. 与代数运算一样,可在表达式中加上多余的括号,使其更清晰。这些括号叫做冗余括号。冗余括号通常用于组合大型表达式中的各个子表达式,使表达式更加清晰。将一条大型语句分割为一系列较短的、较简单的语句,叫做澄清。
  16. 对if结构的主体语句采用缩进,可突出结构主体,并可增强程序可读性。
  17. 在一个程序中,每一行只应有一条语句。
  18. 较长的语句可分割到向个行上。如必须像这样分割一条语句,请挑选最合适的断点。比如对 一个用逗号分隔的列表来说,可选择在某个逗号之后断开;对于较长的表达式,可考虑在一个操作符之后断开等等。一个语句分割成多行后,除第一行之外,其他所有行都进行缩进处理。
  19. 如果一个表达式里有需要包含多个操作符,请务必参考操作符优先级表,核实表达式中的操作符按自己希望的顺序执行。如表达式过于复杂,以至于无法确定顺序,不妨将表达式分割为几个小语句,或者干脆用括号强行规定顺序——这样的做法和代数中无异。其间,请留意某些操作符=)是按从右到左的顺序结合的,而非从左到右。

2008年11月22日星期六

C/C++ COMPILER OPTIONS

cl 编译器的选项:
微软C/C++ 编译器选项
-优化-
/O1 最小化空间 minimize space
/Op[-] 改善浮点数一致性 improve floating-pt consistency
/O2 最大化速度 maximize speed
/Os 优选代码空间 favor code space
/Oa 假设没有别名 assume no aliasing
/Ot 优选代码速度 favor code speed
/Ob 内联展开(默认 n=0) inline expansion (default n=0)
/Ow 假设交叉函数别名 assume cross-function aliasing
/Od 禁用优化(默认值) disable optimizations (default)
/Ox 最大化选项。(/Ogityb2 /Gs) maximum opts. (/Ogityb1 /Gs)
/Og 启用全局优化 enable global optimization
/Oy[-] 启用框架指针省略 enable frame pointer omission
/Oi 启用内建函数 enable intrinsic functions
-代码生成-
/G3 为 80386 进行优化 optimize for 80386
/G4 为 80486 进行优化 optimize for 80486
/GR[-] 启用 C++ RTTI enable C++ RTTI
/G5 为 Pentium 进行优化 optimize for Pentium
/G6 为 Pentium Pro 进行优化 optimize for Pentium Pro
/GX[-] 启用 C++ 异常处理(与 /EHsc 相同) enable C++ EH (same as /EHsc)
/EHs 启用同步 C++ 异常处理 enable synchronous C++ EH
/GD 为 Windows DLL 进行优化 optimize for Windows DLL
/GB 为混合模型进行优化(默认) optimize for blended model (default)
/EHa 启用异步 C++ 异常处理 enable asynchronous C++ EH
/Gd __cdecl 调用约定 __cdecl calling convention
/EHc extern“C”默认为 nothrow extern "C" defaults to nothrow
/Gr __fastcall 调用约定 __fastcall calling convention
/Gi[-] 启用增量编译 enable incremental compilation
/Gz __stdcall 调用约定 __stdcall calling convention
/Gm[-] 启用最小重新生成 enable minimal rebuild
/GA 为 Windows 应用程序进行优化 optimize for Windows Application
/Gf 启用字符串池 enable string pooling
/QIfdiv[-] 启用 Pentium FDIV 修复 enable Pentium FDIV fix
/GF 启用只读字符串池 enable read-only string pooling
/QI0f[-] 启用 Pentium 0x0f 修复 enable Pentium 0x0f fix
/Gy 分隔链接器函数 separate functions for linker
/GZ 启用运行时调试检查 enable runtime debug checks
/Gh 启用钩子函数调用 enable hook function call
/Ge 对所有函数强制堆栈检查 force stack checking for all funcs
/Gs[num] 禁用堆栈检查调用 disable stack checking calls
-输出文件-
/Fa[file] 命名程序集列表文件 name assembly listing file
/Fo 命名对象文件 name object file
/FA[sc] 配置程序集列表 configure assembly listing
/Fp 命名预编译头文件 name precompiled header file
/Fd[file] 命名 .PDB 文件 name .PDB file
/Fr[file] 命名源浏览器文件 name source browser file
/Fe 命名可执行文件 name executable file
/FR[file] 命名扩展 .SBR 文件 name extended .SBR file
/Fm[file] 命名映射文件 name map file
-预处理器-
/FI 命名强制包含文件 name forced include file
/C 不吸取注释 don't strip comments
/U 移除预定义宏 remove predefined macro
/D{=#} 定义宏 define macro
/u 移除所有预定义宏 remove all predefined macros
/E 将预处理定向到标准输出 preprocess to stdout
/I 添加到包含文件的搜索路径 add to include search path
/EP 将预处理定向到标准输出,不要带行号 preprocess to stdout, no #line
/X 忽略“标准位置” ignore "standard places"
/P 预处理到文件 preprocess to file
-语言-
/Zi 启用调试信息 enable debugging information
/Zl 忽略 .OBJ 中的默认库名 omit default library name in .OBJ
/ZI 启用调试信息的“编辑并继续”功能 enable Edit and Continue debug info
/Zg 生成函数原型 generate function prototypes
/Z7 启用旧式调试信息 enable old-style debug info
/Zs 只进行语法检查 syntax check only
/Zd 仅要行号调试信息 line number debugging info only
/vd{01} 禁用/启用 vtordisp disable/enable vtordisp
/Zp[n] 在 n 字节边界上包装结构 pack structs on n-byte boundary
/vm 指向成员的指针类型 type of pointers to members
/Za 禁用扩展(暗指 /Op) disable extensions (implies /Op)
/noBool 禁用“bool”关键字 disable "bool" keyword
/Ze 启用扩展(默认) enable extensions (default)
- 杂项 -
/?, /help 打印此帮助消息 print this help message
/c 只编译,不链接 compile only, no link
/W 设置警告等级(默认 n=1) set warning level (default n=1)
/H 最大化外部名称长度 max external name length
/J 默认 char 类型是 unsigned default char type is unsigned
/nologo 取消显示版权消息 suppress copyright message
/WX 将警告视为错误 treat warnings as errors
/Tc 将文件编译为 .c compile file as .c
/Yc[file] 创建 .PCH 文件 create .PCH file
/Tp 将文件编译为 .cpp compile file as .cpp
/Yd 将调试信息放在每个 .OBJ 中 put debug info in every .OBJ
/TC 将所有文件编译为 .c compile all files as .c
/TP 将所有文件编译为 .cpp compile all files as .cpp
/Yu[file] 使用 .PCH 文件 use .PCH file
/V 设置版本字符串 set version string
/YX[file] 自动的 .PCH 文件 automatic .PCH
/w 禁用所有警告 disable all warnings
/Zm 最大内存分配(默认为 %) max memory alloc (% of default)
-链接-
/MD 与 MSVCRT.LIB 链接 link with MSVCRT.LIB
/MDd 与 MSVCRTD.LIB 调试库链接 link with MSVCRTD.LIB debug lib
/ML 与 LIBC.LIB 链接 link with LIBC.LIB
/MLd 与 LIBCD.LIB 调试库链接 link with LIBCD.LIB debug lib
/MT 与 LIBCMT.LIB 链接 link with LIBCMT.LIB
/MTd 与 LIBCMTD.LIB 调试库链接 link with LIBCMTD.LIB debug lib
/LD 创建 .DLL Create .DLL
/F 设置堆栈大小 set stack size
/LDd 创建 .DLL 调试库 Create .DLL debug libary
/link [链接器选项和库] [linker options and libraries]

名称空间及其前途

---------摘自《C++ Primer Plus》第五版中文版
  随着程序员逐渐熟悉名称空间,将出现统一个编程理念。下面是当前的一些指导原则:
  • 使用在已命名的名称空间中声明的变量,而不是使用外部全局变量。
  • 使用在已命名的名称空间中声明的变量,而不是使用静态全局变量。
  • 如果开发了一个函数库或类库,将其放在一个名称空间中。事实上,C++当前提倡将标准函数库放在名称空间std中,这种做法扩展到了来自C语言中的函数。例如,头文件math.h是与C语言兼容的没有使用名称空间,但c++头文件cmath应将各种数学库函数放在名称空间std中。实际上,并非所有的编译器都完成了这种过渡。
  • 仅将编译指令using作为一种将旧代码转换为使用名称空间的权宜之计。
  • 不要在头文件使用using编译指令。首先,这样做掩盖了要让哪些名称可用;另外,包含头文件的顺序可能影响程序的行为。如果非要使用编译指令using,应将其放在所有预处理器编译指令#include之后。
  • 导入名称时,首选使用作用域解析操作符或using声明的方法。
  • 对于using声明,首选将其作用域设置为局部而不是全局。

别忘了,使用名称空间的主旨是简化大型编程项目的管理工作。对于只有一个文件的简单程序,使用using编译指令并非什么大逆不道的事。

正如前面指出的,头文件名的变化反映了这些变化。老式头文件(如iostream.h)没有使用名称空间,但新头文件iostream使用了std名称空间.

2008年11月20日星期四

何时使用引用参数

-------摘自《c++ primer puls》第五版中文版
使用引用参数的主要原因有两个:
  • 程序员能够修改调用函数中的数据对象.
  • 通过传递引用而不是整个数据对象,可以提高程序的运行速度.

当数据对象较大时(如结构和类对象),第二个原因最重要.这些也是使用指针参数的原因.这是有道理的,因为引用参数实际上是基于指针的代码的另一个接口.那么,什么时候应使用引用、什么时候应使用指针呢?什么时候又应按值传递呢?下面是一些指导原则:

  • 如果数据对象很小,如内置数据类型或小型结构,则按传值传递。
  • 如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向CONST的指针。
  • 如果数据对象是较大的结构,则使用CONST指针或CONST引用,以提高程序的效率。这样可以节省复制结构所需的时间和空间。
  • 如果数据对象是类对象,则使用CONST引用。类设计的语义常常要求使用引用,这是C++新增这项特性的主要原因。因此,传递类对象参数的标准方式是按引用传递。

对于修改调用函数中数据的函数:

  • 如果数据对象是内置数据类型,则使用指针。如果看到诸如fixit(&x)这样的代码(其中x是int型),则很明显,该函数将修改x。
  • 如果数据对象是数组,则只能使用指针。
  • 如果数据对象是结构,则使用引用或指针。
  • 如果数据对象是类对象,则使用引用。

当然,这只是一些指导原则,很可能有充分的理由做出其他的选择。例如,对于基本类型,cin使用引用,因此可以使用cin》n,而不是cin》&n。

2008年11月19日星期三

郭德刚--《善恶图》,经典!

  前几天无聊在优酷看搞笑小视频,无意中看了一段郭德刚的单口相声:《善恶图》,听到最后感觉故事没完。今天又看到了,才发现是这个单口是8段。今天下载了MP3一口气听完,感慨啊,太有才了。。。
  这个单口说的是清朝道光年间北京城发生的一件涉及到22条人命的故事

  详细请找找这几个小段子来听听,不是一般的经典。
下面是一个优酷的专辑:
善恶图

2008年11月14日星期五

初学C++:冒泡排序的写法

  今天看《C++编程金典》数组这一章,后面有个习题:4-11
4-11:图4-16中的冒泡排序并不适合大型数组,。执行以下简单修改提高冒泡排序方法的性能:
A):第一遍排序后,大型数字总会成为数组中编号最大的元素;第二遍排序后,就有两个编号最大的数字,依此类推。这样一来,就不是第次进行9次比较,而是第二遍比较8次,第3遍比交7次,依此类推。
B):数组中的数据可能已经按正确或接近于正确的顺序进行排列,所以如果比较次数较少也能达到理想的效果时,为什么一定要比较9次呢?修改排序方法,查看每次比较后是否数据有无交换,如果没有,那么数据肯定已经采用了正确的排序。如果有交换,则说明至少还需要一次排序。
  4-16的冒泡排序如下


for(int i=0;i<len-1;i++)
{

for(int a=0;a<len-1;a++)
{
if(array[a]>array[a+1])
{
hold=array[a];
array[a]=array[a+1];
array[a+1]=hold;
}
}
}

这个算法,不管怎么样都要执行(len-1)*(len-1)次。

如果按A的算法优化一下就要执行(len-1)*(len-1-(1+len-2)/2)=(len-1)*(len-1)/2次,也就相当于少执行了一半。

如果再按B的算法优化下,如果本来就是一个有序数组的话,仅需执行len-1次,整个降低了一个数量级。当然如果是最糟情况将执行(len-1)*(len-1)次。

A的算法在内循环完成一次就把内循环的记数条件减1就能做到。

增加一个全局变量比如:b=len-1;

然后在外循环中循环条件增加一个b--;

把内循环的执行条件改为j<b;

B的算法,只要内循环中的if 语句条件一次也没有执行就跳出循环便可做到。
增加一个全局变量比如:c=0;
然后把 if语句改成if-else 语句,IF条件没有执行也就相当于else语句执行了b次;
在else语句中加入c++;
然后在外循环处比较c ==b;
如不等则继续循环。如相等就退出循环排序完成。

2008年11月11日星期二

自己动手写:FOURUP 小程序

  学习VC只看书不写代码是不行的,书看得越多代码却不知道怎么写了......
  要不就是一看就明白,可是自己写却什么也写不出来,离了书就不会写了.
  为了避免这种情况一再的发生,写代码就变得非常重要了.
  现在要有一个习惯,看到完整的例子,看懂它,然后用自己所理解的去重写它,要在纸上列出个大纲来,一步一步把这个小例子填写完整,并编译运行,直到没有错误,正确显示为止.但在这个过程中,不要看书,要一气呵成,看一点写一点跟没写一样.

  昨天实在不知道要干什么,找到本电子书随意看了下,发现了一个小例子,叫FOURUP,是<跟我学VC++6>这本书上的一个例子,从第三章到第五章才完全的讲完,例子很简单,算法也很明确,是个用来写代码的好例子.

  首先分析这个例子的需求:
  这人例子大体就是随机出现四幅图片,然后根据这四幅图片的排列,来确定得了多少分。仅此而已。然后就用给出的界面写出它框架来。









里面用的控件,大部分都是装饰,有用的有
1、显示积分数的CStatic,就是100这个东西,书上是把包括四个图标的组框的CAPTION做为重写的东西,我因为变的只有分数,所以只重写数把它们分开了。

2、中间组框中的四个图片控件

3、开始按钮和取消按钮。

界面就是这样,下面再来确定一下要用到的变量:

1、用来存储积分的变量,书上用的的double,但这个一次减2,只是一个测试,所以用int来存储。
2、因为这个程序是用图标资源来做为图片来用的,所有要有四个图标,里面这个是我自己用资源管理器画的,用什么都无所谓的

3、还要一个用来存储某个图标出现了几次的变量,这个用一个int的数组来存储。


下面就来看一下主程序的流程:

主要就是点了开始按钮,积分减2,四个图标随机选择,然后按下面规则来计算得多少分然后加到积分的变量上
用书上的过程来说就是把这些过程再分解:
1、变量减2,这个是开始的条件,应该加个判断,比2小了就不能再玩了,提示或退出
2、显示四个图标。
3、根据显示计算得了多少分
4、把得分显示在控件上。
看了一下上面,还真是够啰嗦了。具体代码就不写了。