c++编程规范.docx

上传人:b****6 文档编号:4082002 上传时间:2022-11-27 格式:DOCX 页数:17 大小:25.09KB
下载 相关 举报
c++编程规范.docx_第1页
第1页 / 共17页
c++编程规范.docx_第2页
第2页 / 共17页
c++编程规范.docx_第3页
第3页 / 共17页
c++编程规范.docx_第4页
第4页 / 共17页
c++编程规范.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

c++编程规范.docx

《c++编程规范.docx》由会员分享,可在线阅读,更多相关《c++编程规范.docx(17页珍藏版)》请在冰豆网上搜索。

c++编程规范.docx

c++编程规范

耀邦软件规范

C++编程规范

版本01

配置管理

文档类别:

耀邦软件规范

文档名称:

C++编码规范

ID:

ybsw-cpp

版本:

01

状态:

草稿

访问路径:

VOB:

ybsw\tech\

模板:

ybsw-tmp-word.dot-01

所有人:

SEPG

作者/创建时间:

2001-10-19

批准人/批准时间:

[谁批准的]

修订历史:

日期

版本

说明

作者

2001-10-19

01

1、

目录

1.简介5

1.1目的5

1.2范围5

1.3读者5

2.代码的组织结构5

2.1接口与实现的分离5

2.2文件的扩展名6

2.3接口6

2.4私有实现的声明7

2.5预处理指令#include8

2.6分离平台依赖性9

3.代码格式9

3.1嵌套语句缩进风格9

3.2函数参数缩进10

3.3代码划分10

4.注释10

4.1注释的风格11

4.2注释的位置11

4.3注释段分离11

4.4避免冗余12

4.5自记录代码12

4.6一致性13

4.7文件注释13

4.8类的注释13

4.9函数注释13

5.命名14

5.1原则14

5.1.1避免使用只靠字母大小写才能区分的名称14

5.1.2避免使用缩写14

5.1.3选择清晰的、易辨认的、有意义的名称14

5.2文件命名15

5.3类15

5.4函数16

5.5变量17

6.条件编译17

6.1防止文件重复包含17

6.2内嵌函数文件18

7.检查列表19

C++编程规范

1.简介

目的

大型团队生成的代码要有项目范围内可评测的质量,代码必须遵从于某一标准并以此来评价,编程活动也需得要规范和指导。

本文档为使用C++进行程序开发提供编程规范。

使得以下目的成为可能:

♦增加开发过程代码的强壮性、可读性、易维护性;减少有经验和无经验开发人员编程所需的脑力工作。

♦通过人为以及自动的方式对最终软件应用质量标准

♦使新的开发人员快速适应项目氛围。

范围

本文档仅包含使用C++进行编程时编程规范,不包括C++编程指南,及设计指南。

读者

所有以C++进行开发的软件开发人员应该阅读文档,并依照本文档指导程序的开发。

2.代码的组织结构

开发大型系统时通常将它分成许多小的功能子系统。

子系统本身由许多代码模块组成。

在C++中,一个模块通常包含了实现单一一个抽象,少数情况下,也可能是一组紧密相关的抽象。

C++中,一个抽象通常实现为一个类。

一个类有两个不同的构件:

一个是对类客户可见的接口,它提供了类能力和责任的声明或规约;另一个是所声明规约(类定义)的实现。

与类相似,模块也有它的接口和实现:

模块接口包括对所包含模块抽象(类声明)的规约;模块实现包括对抽象(类定义)的实际实现。

在系统构造时,可将子系统组织成协作的组或层来减少或控制它们间的依赖性。

接口与实现的分离

模块的接口应放置在与模块实现文件不同的文件中,此文件指头文件。

模块的实现文件可能放置于一个或多个实现文件中。

如果模块实现包括扩展内嵌函数、普通私有实现声明、测试代码或具体平台的代码,那就把这些部分分开存储,并以那部分内容来命名。

如果程序的可执行大小是一个要考虑的问题,则不常用到的函数应当置于它们各自的文件中。

选择一个模块划分和命名的机制,使用它要前后一致。

示例

SymaNetwork.hh//包括类

//“SymaNetwork”的声明。

SymaNetwork.Inlines.cc//内嵌定义子单元

SymaNetwork.cc//模块的主要实现单元

SymaNetwork.Private.cc//私有实现子单元

SymaNetwork.Test.cc//测试代码子单元

基本原理

从模块的实现中分离出其接口有助于用户和提供者代码的独立开发。

将一个模块的实现分割成多个变换单元,这可以更好的支持删除对象代码,并会导致更小的可执行大小。

使用规范化的文件命名与划分约定允许在不检查模块的实际内容时理解其内容与组织。

将名称从代码让渡给文件名增加了可预测性,有助于建立基于文件的工具而不需要复杂命名映射。

文件的扩展名

常用的文件扩展名:

对头文件,为.h、.H、.hh、.hpp和.hxx;对实现文件,为.c、.C、.cc、.cpp和.cxx。

选择一组扩展名后,使用时要前后一致。

示例

SymaNetwork.hh//扩展名“.hh”用来指定

//一个“SymaNetwork”模块的接口(头)文件。

SymaNetwork.cc//扩展名“.cc”用来指定

//一个“SymaNetwork”模块的实现(实施)文件

接口

每个模块接口避免定义多个类,减少模块的接口以及其他模块对此接口的依赖。

只有在极少数情况下多个类才能置于同一个模块中;此时他们必须是紧密关联关系的(如容器与它的迭代程序)如果所有类都需要对客户模块是可见的,可以把模块的主要类及其支持类置于同一个头文件中。

私有实现的声明

避免将私有实现声明置于模块的接口中,除了类的私有成员以外,模块的私有实现声明(如私有实现类型和支持类)不能出现在模块的接口中。

如果单个实现文件需要这些声明,它们应当置于所需的实现文件中;

如果有多个实现文件,则这些声明应置于辅助私有头文件中。

而其他实现文件在需要时应包含这个辅助私有头文件。

这一做法保证了:

模块接口清楚的表示了它的抽象,不再需要实现工件;

模块接口越小越好,并因此减小了模块间的编译依赖;

示例

//模块foo的规约,包含于文件“foo.hh”中;

//

classfoo

{

..声明

};

//“foo.hh”结束;

//模块foo的私有声明,包含于文件

//“foo.private.hh”中;所有foo实施文件都使用它。

...私有声明

//“foo.private.hh”结束;

//模块foo的实施,包含于以下多个文件中

//“foo.x.cc”和“foo.y.cc”

//文件“foo.x.cc”;

//

#include"foo.hh"//包含模块本身的头文件

#include"foo.private.hh"//包含实施

//所需的声明。

...定义

//“foo.x.cc”结束

//文件“foo.y.cc”;

//

#include"foo.hh"

#include"foo.private.hh"

...定义

//“foo.y.cc”结束

预处理指令#include

模块要使用另一模块,则必须使用预处理#include指令来访问提供者模块的接口。

相应的,模块不能再次声明提供者模块接口的任何一部分。

只存在唯一的模块接口声明,所有的客户只看到同一接口。

当包含文件时,只有对“标准”头文件使用#include

语法;对其余的使用#include"header"语法。

使用#include指令也适用于模块自身的实施文件:

模块实施文件必须使用#include指令包括它本身的规约和私有辅助头文件,

示例:

//模块foo头文件中的规约

//"foo.hh"

//

classfoo

{

...声明

};

//“foo.hh”结束;

//模块foo在文件“foo.cc”中的实施;

//

#include"foo.hh"//实施文件包含

//它本身的规约

...foo成员的定义

//“foo.cc”结束

分离平台依赖性

从独立于平台的代码中分离出依赖于平台的代码;这有助于提高可移植性。

依赖于平台的模块应当具有受平台名限制的文件名以突出对平台的依赖。

示例:

SymaLowLevelStuff.hh//“LowLevelStuff”

//规约

SymaLowLevelStuff.SunOS54.cc//SunOS5.4实施

SymaLowLevelStuff.HPUX.cc//HP-UX实施

SymaLowLevelStuff.AIX.cc//AIX实施

3.代码格式

嵌套语句缩进风格

使用一致的缩进来清楚的界定嵌套语句;为达到这一目的,两到四个空格的缩进是最有效的方式。

推荐使用规则的两空格缩进。

垂直布置分界符{};混合语句或块语句的分界符({}),应当与周围的语句处于缩进的同一级上。

通过选择空格的数量对块内的语句进行缩进。

switch语句的case标号应当与switch语句处于同一缩进级;switch语句内语句应比switch语句本身和case标号多缩进一级。

示例:

if(true)

{//新程序块

foo();//程序块内语句

//缩进两个空格

}

else

{

bar();

}

while(expression)

{

statement();

}

switch(i)

{

case1:

do_something();//相对switch语句本身

//缩进

break;//一层

case2:

//...

default:

//...

}

函数参数缩进

如果一行之内难以容下函数声明,则将函数的第一个参数放在函数名所在的那一行;其余参数每个占一新行,使用与第一个参数相同的缩进。

下面所示的声明与缩进的风格,在函数返回类型和名称下留有空格;因此,提高了它们的可见性。

enum声明也同样,当参数列表不能置于在一行里。

采用下面示例的方式。

示例:

voidfoo:

:

function_decl(some_typefirst_parameter,

some_other_typesecond_parameter,

status_typeand_subsequent);

代码划分

使用空行而不是多个注释行来分割相关的源代码块。

使用一个空行来分离函数或类中的结构。

使用两个空行将函数与函数相分离。

4.注释

注释的风格

应使用C++风格注释分界符"//",而非C风格的"/*...*/"。

C++风格的注释更易理解,它减少了由于偶然缺少一个注释结束分隔符而造成大量代码被注释掉的风险。

注释的位置

注释应紧贴它们所要注释的代码;它们使用相同的缩进。

对多行连续语句的注释应置于语句的上方作为语句的介绍性说明。

而对单个语句的注释应置于语句的下方。

注释应避免与源结构处于同一行:

否则会使注释与其所注释的源代码不对齐。

示例:

//多行连续语句的注释应置于语句的上方

do

{

count++;

//对单个语句的注释应置于语句的下方

...

}while();

在描述长声明中的元素(如enum声明中的枚举操作符)时,应在行末注释。

enumcolor{red,

orange,

yellow,

green,//行末注释的例子...

violet};

注释段分离

在一个注释块中,使用空注释行而非空行来分割注释段

示例:

//在下一段中

//需要继续这里的解释

//

//空注释行使得

//同一注释块中

//不同段间的界限分明。

避免冗余

注释中应避免重复程序标识符,避免复制别处有的信息(此时可使用一个指向信息的指针)。

否则程序中的任何一处改动都可能需要多处进行相应的变动。

如果其他地方没有进行所需的注释改动,将会导致误注释:

这种结果比根本没有注释还要糟糕。

不要注释太明显的东西。

注释应当作为源代码的补充,而不是直译源代码。

它们应当解释不能直接从源代码看出东西;它们不应复制语言的语法或语义。

它们应当帮助读者掌握背景中的概念、依赖性、特别是复杂的数据代码和算法。

它们应当突出:

与代码或设计标准的不同点、受限特性的使用、以及特殊的“技巧”。

错误示例:

zerocount++;//变量加1

自记录代码

时刻注意要编写自记录代码而非注释。

这可通过选择合理的名称、使用特殊的临时变量或重新安排代码的结构来实施。

注意注释中的风格、语法和拼写。

使用自然语言注释而不是电报或加密格式。

(不推荐风格)示例

do

{

...

}while(string_utility.locate(ch,str)!

=0);

//当找到时退出查找循环。

(推荐风格)示例

do

{

...

found_it=(string_utility.locate(ch,str)==0);

}while(!

found_it);

一致性

在写注释的时候应该注意不要与代码矛盾。

修正代码的时候应该同时修正注释。

与代码脱节的注释不如不要。

对差的代码不要进行复杂的注释,建议重写。

文件注释

对一个文件的注释需要至少4部分。

其他信息可自行考虑增加注释行。

第一行(部分)注释指出文件名

第二行(部分)注释注明作者和最后修改时间(参考信息)

第三行(部分)注释对整个文件的用途大致说明

第四行(部分)注释版权信息

类的注释

对一个类的注释需要至少4部分。

其他信息可自行考虑增加注释行。

第一行(部分)注释指出类的名

第二行(部分)注释类的目的

第三行(部分)注释异常

第四行(部分)注释版权信息

函数注释

对一个函数的注释需要至少4部分。

其他信息可自行考虑增加注释行。

第一行(部分)注释指出函数的名

第二行(部分)注释函数功能

第三行(部分)注释函数参数

第四行(部分)注释函数的返回

5.命名

本章为程序实体(类、函数、类型、对象、常量、异常、名字空间)提供一个名称约定的建议。

原则

避免使用只靠字母大小写才能区分的名称

只通过大小写才能区分的类型名称,它们间的差异是很难记忆的,也就很容易造成混淆。

避免使用缩写

应用领域中常用的缩写(如FFT指快速傅立叶变换),或在项目缩写清单中有定义的缩写,才能使用相应的缩写。

否则,很有可能相似但不相同的缩写到处出现,之后就引进了混淆和错误(如将track_identification缩写成trid、trck_id、tr_iden、tid、tr_ident等)。

选择清晰的、易辨认的、有意义的名称

从应用的角度选择名称,名词使用形容词修饰来提高局部(具体上下文)的含义。

确保名称与其类型保持一致。

编译器和支持的工具有时限制名称的长度。

因此,应当注意:

长名称不应只在结尾的几个字符有所不同,因为结尾的几个用于区分的字符可能会被这些工具截断。

使用名词和形容词构成名称、确保名称与类型相符、遵循自然语言以增强代码的可读性和语义。

示例:

voidset_color(colornew_color)

{

...

the_color=new_color;

...

}

文件命名

以模块的主要抽象名作为模块名。

为模块名附加一部分类型名。

选择暗示它们类型的部分类型名。

模块名和部分名由分隔符分隔(如“_”(下划线)或“.”(句点));选择一个分隔符,使用它要前后一致。

File_Name:

:

=[]'.'

为了更好的预测,对文件名使用相同的大小写,这与代码内名称的使用相同。

以下是模块划分与命名策略的示例:

module.inlines.cc-如果一个模块有多个潜在的内嵌函数,就将函数的定义置于一个单独的内嵌文件中。

module.private.hh-如果模块有许多常用的被其他部分引用的私有实现声明,就把这些声明分隔出去组成一个私有部分,它可由其他实施文件包含。

module.private.cc-模块的私有实施函数定义,为编辑的方便将它分离出去。

module.function_name.cc-如果可执行的大小是个要考虑的问题,应当分离出许多程序不需要的特殊成员函数,组成各自的实施文件。

如果重载的函数置于不同文件中,每一文件函数名都应有一实例数字的后缀。

如,function_name1表示第一个独立重载函数实例。

module.nested_class.cc-模块嵌套类的成员函数,置于其本身文件中。

module.test.[hh\cc]-如果一个模块需要扩展测试代码,则测试代码必须在友测试类中予以声明。

友测试类应称为Module.Test。

将测试代码声明为友类有助于模块及其测试代码的独立开发;这还允许测试代码从最终模块对象代码中删除而源并不改变。

module.platform_name.cc-分离出任意模块的平台依赖性并以平台名称命名部分名称。

使用简单形式的常用名词或名词短语,为类取名时要表达出它的抽象含义。

基类使用更通用的名称,而对派生类使用更专用的名称。

typedef...reference;//出自标准库

typedef...pointer;//出自标准库

typedef...iterator;//出自标准库

classbank_account{...};

classsavings_account:

publicbank_account{...};

classchecking_account:

publicbank_account{...};

当对象和类型的名称冲突或缺少合适的名称时,对象使用简单名称,类型名称添加后缀mode、kind、code等。

表明是对对象集合的抽象时使用复数形式。

typedefsome_container<...>yellow_pages;

当需要对象集合外的其他语义时,使用以下标准库中的数据结构作为行为模式和名称后缀:

vector(向量)-一种可随机访问的顺序容器;

list(表)-一种有序的顺序容器;

queue(队列)-一种先入先出的顺序容器;

deque(双端队列)-一种两个端口都可进行操作的队列;

stack(堆栈)-一种后入先出的顺序容器;

set(集合)-一种关键词访问(关联关系)容器;

map(图)-一种关键词访问(关联关系)容器;

函数

对无返回值的函数(函数声明为void返回类型)或返回值使用指针或引用类型的函数,使用动词或动词短语。

对使用非void返回类型返回唯一一个值的函数,使用名词或名词短语。

对带有常用操作(行为模式)的类,使用项目选项列表中的操作名。

例如:

begin,end,insert,erase(标准库中的容器操作)。

返回布尔值(谓词)的函数使用形容词(或过去分词)。

谓词经常使用名词前添加前缀is或has的形式使名称表达一个肯定的声明。

当对象、类型名或枚举型常量也使用简单名称时,这也同样非常有用。

时态上要保持一致,力求准确。

示例

voidinsert(...);

voiderase(...);

Namefirst_name();

boolhas_first_name();

boolis_found();

boolis_available();

不要使用否定意义的名称,因为这会导致双重否定表达式出现(如!

is_not_found);这使得代码更难以理解。

有些情况下,通过使用反义词,如“is_invalid”代替“is_not_valid”,可以使否定谓词变成肯定的而不需改变其语义。

示例

boolis_valid(...);

voidfind_client(namewith_the_name,bool&found);

变量

为表明其唯一性或表明本实体是活动的主要核心,在对象或参数名称前加前缀“the”或“this”。

要表明次要、临时、辅助对象,则加前缀“a”或“current”:

示例

voidchange_name(subscriber&the_subscriber,

constsubscriber:

:

namenew_name)

{

...

the_subscriber.name=new_name;

...

}

voidupdate(subscriber_list&the_list,

constsubscriber:

:

identificationwith_id,

structure&on_structure,

constvaluefor_value);

voidchange(object&the_object,

constobjectusing_object);

6.条件编译

防止文件重复包含

通过在每个头文件中使用以下结构可以防止重复包含与编译文件:

#ifndef(module_name)//使用预处理符

#definemodule_name//防止重复

//包含...//声明到此

#include"module_name.inlines.cc"//可选的内嵌结构

//包含到此。

//包含模块内嵌函数之后

//没有其他声明。

#endif//module_name.hh结束

对包含保护符使用模块文件名。

对模块名与操作符使用相同大小写。

内嵌函数文件

将模块内嵌函数的定义置于单独的文件中。

当模块有许多内嵌函数时,它们的定义应置于一个分离的只有内嵌函数的文件中。

在模块接口文件的末尾应包含内嵌函数文件。

内嵌函数声明文件建议的名字为module_name.inlines.hh。

要求使用"No_Inline"条件编译符来做可选择的内嵌编译。

如下:

使用条件编译结构控制可内嵌函数的内嵌(相对于外部的)编译。

//在文件module_name.inlines.hh首部,

#ifndef(module_name_inlines)

#definemodule_name_inlines

#ifdef(No_Inline)

#defineinline//使内嵌关键词无效

#endif

...//内嵌定义到此

#endif//文件module_name.inlines.hh结束

//在文件module_name.hh尾

//

#ifndef(No_Inline)

#include"module_name.inlines.hh"

#endif

//包含module_name.hh之后

//module_name.cc文件首

//

#ifdef(No_Inline)

#include"module_name.inlines.hh"

#endif

如果未定义No_Inline,则内嵌函数文件module_name.inlines.hh在模块接口中包含。

如果定义了No_Inline,则模块接口不包含内嵌定义,但模块实现包含了它。

条件编译增加了维护构建依赖关系的复杂性。

对这种复杂性的管理通常是利用把头文件和内嵌函数定义视为一个逻辑单元:

实施文件因此依赖于头文件以及内嵌函数定义文件。

 

参考资料与术语表

参考资料:

名称

ID

版本

存储路径

术语表:

名称

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 初中教育 > 政史地

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1