资源描述
C+编程风格培训,东信北邮信息技术有限公司,培训目的,熟练掌握基本的C+编程规范 正确使用C+语言的特性 了解一些基本的C+的编程技巧,内容简介,文件结构 程序版式 基本命名规则 表达式和基本语句 函数设计 内存管理 C+的特性 其他的一些注意事项,文件结构,基本概念(文件结构 ),每个C+/C程序通常分为两个文件。一个文件用于保存程序的声明(declaration),称为头文件。另一个文件用于保存程序的实现(implementation),称为定义(definition)文件。 C+/C程序的头文件以“.h”或“.hpp”为后缀,C程序的定义文件以“.c”为后缀,C+程序的定义文件通常以“.C”为后缀(也有一些系统以”.cpp”,“.cc”或“.cxx”为后缀)。,版权和版本申明(文件结构),/* * Copyright(C) 2000 EASTCOM-BUPT Inc. * * Filename : smapcommand.h * Description : header file of smapcommand.C * * Author : Li Wei * * History : 2000-07-24 Created * */,头文件的结构(文件结构),为了防止头文件被重复引用,应当用ifndef/define/endif结构产生预处理块。 用 #include 格式来引用标准库的头文件(编译器将从标准库目录开始搜索)。 用 #include “filename.h” 格式来引用非标准库的头文件(编译器将从用户的工作目录开始搜索)。 头文件中只存放“声明”而不存放“定义” 不提倡使用全局变量。,定义文件的结构(文件结构),#ifndef GRAPHICS_H / 防止graphics.h被重复引用 #define GRAPHICS_H #include / 引用标准库的头文件 #include “myheader.h” / 引用非标准库的头文件 void Function1(); / 全局函数声明 class Box / 类结构声明 ; #endif,目录结构,如果一个软件的头文件数目比较多(如超过十个),通常应将头文件和定义文件分别保存于不同的目录,以便于维护。例如可将头文件保存于include目录,将定义文件保存于source目录(可以是多级目录)。 如果某些头文件是私有的,它不会被用户的程序直接引用,则没有必要公开其“声明”。为了加强信息隐藏,这些私有的头文件可以和定义文件存放于同一个目录。,程序版式,空行 (程序版式),在每个类声明之后、每个函数定义结束之后都要加空行 。 在一个函数体内,逻辑上密切相关的语句之间不加空行,其它地方应加空行分隔 。,空行 (程序版式),/ 空行 void Function1() / 空行 void Function2() / 空行 void Function3() ,空行 (程序版式),/ 空行 while (condition) statement1; / 空行 if (condition) statement2; else statement3; / 空行 statement4; ,代码行 (程序版式),一行代码只做一件事情,如只定义一个变量,或只写一条语句。这样的代码容易阅读,并且方便于写注释。 if、for、while、do等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加。这样可以防止书写失误。 尽可能在定义变量的同时初始化该变量,代码行 (程序版式),/风格一 if (width height) dosomething(); /风格二 if (width height) dosomething(); int width = 10; / 定义并初绐化width,长行拆分(程序版式),代码行最大长度宜控制在70至80个字符以内。代码行不要过长,否则眼睛看不过来,也不便于打印。 长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。,长行拆分(程序版式),if (variable1 =variable12) ,缩进(程序版式),不要使用TAB键,最好使用4个空格作为缩进,注释(程序版式),边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。 注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不可放在下方。 如果代码本来就是清楚的,则不必加注释。否则多此一举,令人厌烦。 比如: j+; / 大家注意啦:j的值加一啦,注释(程序版式),函数的注释,应该可以尽量清晰的说明函数的功能,输入输出参数。 比如: /*-*/ /* 函数名称:operatorLogin */ /* 功能描述:处理操作员登陆 */ /* 参数说明: */ /* data:操作员登陆的命令对象指针 */ /* 返回值: */ /* 备注: */ /*-*/ void operatorLogin(ScDataType *data),注释(程序版式),修改注释,应该用简洁的语言说明修改人(全名),修改时间和修改目的。 比如: /age=23 / lugang,2002-07-07 / 因为过去了一年,所以该参数值加一 age=24,类的版式 (程序版式),将public类型的函数写在前面,而将private类型的数据写在后面 。,类的版式 (程序版式),class A public: void Func1(void); void Func2(void); protected: int h; private: int i, j; float x, y; ,命名规则,命名规则,标识符应当直观且可以拼读 命名规则尽量与所采用的操作系统或开发工具的风格保持一致。 程序中不要出现仅靠大小写区分的相似的标识符。 void foo(int x);/ 函数foo 与FOO容易混淆 void FOO(float x);,命名规则,程序中不要出现标识符完全相同的局部变量和全局变量,尽管两者的作用域不同而不会发生语法错误,但会使人误解。 如果标识符有多个单词组成,每个单词的第一个字母应大写(首单词除外),如果是缩写则缩写的第一个字母大写,其余的小写。 比如: int oldValueCode; int oldHlrCode; /HLR是缩写,命名规则,变量的名字应当使用“名词”或者“形容词名词”。 float value; float oldValue; 全局函数的名字应当使用“动词”或者“动词名词”(动宾词组)。类的成员函数应当只使用“动词”,被省略掉的名词就是对象本身。 drawBox(); / 全局函数 box-draw(); / 类的成员函数,命名规则,类名用大写字母开头的单词组合而成。 例如: class Node; / 类名 class LeafNode; / 类名 函数名用小写字母开头的单词组合而成。 void draw(void); / 函数名 void setValue(int value); / 函数名,命名规则,变量和参数用小写字母开头的单词组合而成。 例如: BOOL flag; int drawMode; 常量全用大写的字母,用下划线分割单词。 例如: const int MAX = 100; const int MAX_LENGTH = 100;,命名规则,静态变量加前缀s_(表示static)。 如果不得已需要全局变量,则使全局变量加前缀g_(表示global)。 类的数据成员加前缀m_(表示member),这样可以避免数据成员与成员函数的参数同名。,表达式和基本语句,if 语句 (表达式和基本语句 ),不可将布尔变量直接与TRUE、FALSE或者1、0进行比较。 if (flag) / 表示flag为真 if (!flag) / 表示flag为假,if 语句 (表达式和基本语句 ),应当将整型变量用“=”或“!=”直接与0比较。 假设整型变量的名字为value,它与零值比较的标准if语句如下: if (value = 0) if (value != 0) 不可模仿布尔变量的风格而写成 if (value) / 会让人误解 value是布尔变量 if (!value),if 语句 (表达式和基本语句 ),不可将浮点变量用“=”或“!=”与任何数字比较,应该设法转化成“=”或“=-EPSINON) & (x=EPSINON) 其中EPSINON是允许的误差(即精度)。,if 语句 (表达式和基本语句 ),应当将指针变量用“=”或“!=”与NULL比较。 比如: char *p=NULL; if (p!=NULL) delete p;,switch语句(表达式和基本语句 ),每个case语句的结尾不要忘了加break,否则将导致多个分支重叠(除非有意使多个分支重叠)。 不要忘记最后那个default分支。即使程序真的不需要default处理,也应该保留语句,switch语句(表达式和基本语句 ),int value; . switch (value) 1: break; 2: . . default : ,函数设计,参数的设计(函数设计),一个函数的代码短的行数不应该超过80行,如果超过了,这个函数的设计存在一定问题。 避免函数有太多的参数,参数个数尽量控制在5个以内。如果参数太多,在使用时容易将参数类型或顺序搞错。,返回值的规则 (函数设计),不要省略返回值的类型。 不要将正常值和错误标志混在一起返回。正常值用输出参数获得,而错误标志用return语句返回。,内存管理,从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。,内存分配方式(内存管理),常见的内存错误及其对策,用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。 不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。 避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。,常见的内存错误及其对策,动态内存的申请与释放必须配对,防止内存泄漏。 用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。 new和 delete,以及malloc和free必须配套使用,不可混用。 如果对一个空指针进行free或delete操作,系统不会报错,程序也不会异常终止。,常见的内存错误及其对策,内存拷贝函数memcpy要慎用 比如: char str210=“hello lg”; char str16; int i=1; memcpy(str1,str2,10);,动态内存会被自动释放吗?,void Func(void) char *p = (char *) malloc(100); / 动态内存会自动释放吗? ,内存耗尽怎么办?,判断指针是否为NULL,如果是则马上用return语句终止本函数。 判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行。 为new和malloc设置异常处理函数。 例如可以用set_new_hander函数,C+的特性,类的构造和析构函数,类的成员变量初始化应该尽量放在构造函数中进行; 类的析构函数中应该释放所有在该类的中分配的动态内存;,类的构造和析构函数,比如:ClassA的declaration如下: class ClassA public: ClassA(); ClassA(); private: int distrubutr1; bool ifInitial; char *buffer; ;,类的构造和析构函数,ClassA的definition如下: ClassA:ClassA() distrubutr1=0; ifInitial=true; p=NULL; ClassA:ClassA() delete buffer; ,其他特性,函数和运算符重载 Override Inline的使用 拷贝构造函数 继承和组合 Const的使用,其他的一些注意事项,其他的一些注意事项,当心那些视觉上不易分辨的操作符发生书写错误。我们经常会把“”误写成“”,象“|”、“&”、“=”这类符号也很容易发生“丢1”失误。然而编译器却不一定能自动指出这类错误。 变量(指针、数组)被创建之后应当及时把它们初始化,以防止把未被初始化的变量当成右值使用。,其他的一些注意事项,当心数据类型转换发生错误。尽量使用显式的数据类型转换(让人们知道发生了什么事),避免让编译器轻悄悄地进行隐式的数据类型转换。 当心变量发生上溢或下溢,数组的下标越界。 当心忘记编写错误处理程序,当心错误处理程序本身有误。,其他的一些注意事项,避免编写技巧性很高代码。 不要设计面面俱到、非常灵活的数据结构。 如果原有的代码质量比较好,尽量复用它。但是不要修补很差劲的代码,应当重新编写。 如果你是对于其他人编写的代码进行修改,应该尽量按照原有的代码编写风格修改代码。,推荐网址, www.gnu.org 有一些有用的书籍和源码,推荐书籍和工具,推荐C+源码的阅读工具SourceInsight 推荐书籍: 高质量C+编程指南 C+编程思想,谢谢 问题? 我的信箱: lugang,
展开阅读全文