高质量编程技巧
编写目的:
初入公司开始真正的程序员的生活,为了便于交流,希望自己写的程序可以被别人采纳,为了自己以后的发展故在这里学习一下林锐博士的《c/c++高质量编程》。
第一章:文件结构
C++/C程序的文件以”.h”为后缀,C程序的定义文件以”.c”为后缀,C++程序的定义文件通常以”.cpp”为后缀也有一些系统以”.cc”或”.cxx”为后缀。
在头文件(.h)和实现(.cpp定义)的时候要写好版权和版本的声明主要内容由四个组成:
(1) 版权信息
(2) 文件名称,标识符,摘要
(3) 当前版本号,作者/修改者,完成日期
(4) 版本历史信息。
如下:
/*
*copyright(c) 2001 ,什么公司
*All rights reserved
*
*文件名称 filename.h
*文件标识:见配置管理计划书
*摘要:简要描述本文件的内容
*
*当前版本:1.1
*作者:输入作者(或修改者)名字
*完成日期:
*
*取代版本1.0
*原作者:
*完成日期:
*/
1、头文件的结构(.h):
(1)头文件开头处的版权和版本声明
(2)预处理块。(ifndef/define/endif)
(3)函数和类结构声明等。
规则1:为了防止文件被重复引用,应当用ifndef/define/endif结构产生预处理块。
规则2:#include <filename.h>引用标准库的头文件(编译器将从标准库目录开始搜索)。
规则3:#include “filename.h”引用非标准库的头文件(编译器将从用户的工作目录开始搜索)。
规则4:头文件中只存放“声明”而不存放“定义”。
如下:
// 版权和版本声明见示例1-1,此处省略。
#ifndef GRAPHICS_H // 防止graphics.h被重复引用
#define GRAPHICS_H
#include <math.h> // 引用标准库的头文件
…
#include “myheader.h” // 引用非标准库的头文件
…
void Function1(…); // 全局函数声明
…
class Box // 类结构声明
{
…
};
#endif
2、定义文件的结构(.cpp)
(1)定义文件开头处的版本和版本声明
(2)对一些头文件的引用
(3)程序的实现体
如下:
// 版权和版本声明见示例1-1,此处省略。
#include “graphics.h” // 引用头文件
…
// 全局函数的实现体
void Function1(…)
{
…
}
// 类成员函数的实现体
void Box::Draw(…)
{
…
}
第二章 程序的版式
1、空行:
(1)规则1:在第个类声明之后、每个函数定义结束之后都要加一个空行
(2)规则2:在一个函数内逻辑密切的相关语句不要加空行,其它要加空行。
(3)规则3:一行代码只做一件事
(4)规则4:if、for、while、do等语句自占一行,执行语句不得紧跟其后。
(5)规则5:尽可能在定义变量时初始化变量(就近原则)
2、空格:
(1)规则1:关键字后要留空格。
(2)规则2:函数名之后不要留空格,紧跟左括号,以与关键字区别。
(3)规则3:“,”之后要留空格,“;”不是一行的结束符后要有空格
例:for (int i=0; i<MAX; i++){}
(4)规则4:赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符等 二元操作符后的前后要加空格。
(5)规则5:一元操作符如“!”,“~”,“++”,“&”等前后不加空格。
3、对齐:
(1)规则1:左右括号对齐
4、注释:
(1)规则1:注释放在代码的上方或右方,不可以放在下方。
(2)规则2:当代码跨行多时应该在最后一个括号的时候写清楚是什么的结束。
5、命名规则:
(1)规则1:Window是“大小写”混排方式,而Unix应用程序的标识是“小写加 下划线”的方式。
(2)规则2:变量的字句应当使用“名词”或者“形容词+名词”。
(3)规则3:函数的名字应当使用“动词”或者“动词+名词”。在类中成员函数应 当只使用“动词”,被省略掉的名词就是对象本身。
(4)规则4:类名和函数名用大写字母开头的单词组合而成。
(5)规则5:变量和参数用小写字母开头的单词组合而成。
(6)规则6:常量全用大写的字母,用下划线分割单词。
(7)规则7:静态变量加前缀s_(表示static).
(8)规则8:全局变量用g_(表示global)
(9)规则9:类的数据成员加前缀m_表示(member)这样可以避免数据成员与成 员函数的参数同名。
第四章 表达式和基本语句
1、布尔变量与零值比较:
不可将布尔变量直接与TRUE、FALSE或者1、0进行比较。应该用:
If(flag)或if(!flag)
2、整型变量与零值比较:
应当将整型变量用“==”“!=”直接与0比较。
3、浮点变量与零值比较:
不可将浮点变量用“==”或“!=”与任何数字比较。应该用:
If((x>=-EPSIONON)&&(x<=EPSINON))
4、指针变量与零值比较
应当将指针变量用“==”或“!=”与NULL比较。
5、if语句的判断:
应该用if(NULL==P)这样可以避免比较错误
6、循环语句的效率:
1)在多重循环中,如果有可能,应当将最长的循环放在量内层,最短的循环放在最外 层,以减少cpu跨切循环层的次数。
2)如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体外面。 因为放在内部逻辑判断会打断循环“流水线”作业。效率不高。
第五章 常量
1、 const 与#define 的比较
const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全 检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生 意料不到的错误。
在C++程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量
2、类中的常量:
Const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,因 为类可以创建多个对象,不同的对象基const数据成员的值可以不同。
不能在类声明中初始化const数据成员。
错误:
Class A
{
Const int SIZE =100;//错误:企图在类声明中初始化const数据成员
}
Const数据成员的初始化只能在类构造函数的初始化表中进行
Class A
{
A(int size);//构造函数
Const int SIZE;
}
A::A(int size):SIZE(size)//构造函数的初始化表
{……
}
A a(100);//对象a的SIZE值为100
以上就是常理在类中的使用,不同对象有不同的常量的原因
要建立在整个类中都恒定的常量,别指望const数据成员了,应该用类中的枚举常量来实现例如:
Class A
{
Enum{SIZE1 = 100, SIZE2 = 200};//枚举常量
Int array1[SIZE1];
Int array1[SIZE2];
};
枚举常量不会占用对象的存储空间,它们在编译时被全部求值。枚举常量的缺点是: 它的隐含数据类型是整数,其最大值有限,且不能表示浮点数
第六章
函数接口的两个要素是返回值和参数。
1、 参数的规则:
规则1:如果参数是指针,且仅作为输入用,则应在类型前加const,以防止该指 针在函数体内被意外修改。
例:void StringCopy(char* strDestination,const char* strSource);
规则2:如果输入参数以值传递的方式传递对象,则宜用(const TypeName& 参数 名)方式来传递,这样可以省去临时对象的构造和析构过程,从而提高效率。
2、 函数内部实现规则:
规则1:在函数的“入口处”,对参数的有效性进行检查,要会使用assert(断言)。
规则2:在函数返回是不可以返回“栈内存”即在函数内开辟的“指针”和“引用”, 因为该内存在函数体结束时被自动销毁。要考虑return的语句效率。
3、 使用断言:
断言assert仅在Debug中起作用的宏,assert(false)会报警。
4、 引用与指针的比较
引用的规则:
1) 引用被创建的时候必须被初始化。
2) 引用不能有NULL引用,引用必须与合法的存储单元关联(指针是可以是NULL)。
3) 一旦引用被初始化,就不能改变引用的关系(指针则可以改变所指对象)。
第七章
1、内存分配方式:
(1)从静态存储区域分配。内存在程序编译的时候就把空间分配好了,这块内存空间 在整个程序运行时都存在,例:static 变量
(2)在栈上创建。在执行函数里,函数内部变量都可以在栈上创建,函数执行结束时 他们会变释放。栈内分配运算内置于处理器的指令集中,效率很高,但是分配的 内存容量有限。
(3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意 多少的内存,程序员自己负责在何时用free或delete释放内存。
2、内存错误:
规则1:内存分配后要初始化。
规则2:内存用完要释放
规则3:释放了内存不可以使用,return 不要返回指向“栈内存”的“指针”
规则4:释放后没有将指针设置为NULL。导致产生“野指针”。
规则5:用malloc和new申请内存后,应该立即检查指针是否为NULL防止使用指针值为NULL的内存。
规则6:不要忘记为数组和动态内存赋初值。
规则7:动态内存的申请与释放必须配对。
3、指针与数组的对比:
数组要么在静态存储区被创建,要么在栈上被创建。数组名对应着一块内存,基地址与容量在生命期内保持不变,只有数组的内容可以改变。
指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。
4、malloc/free和new/delete的区别
Malloc 使用的时候不会调用构造函数而new会
Free 使用的时候不会调用析构函数而delete会
在new 数组时要delete[] 指针数组