高质量C++/C 编程指南一

首先,强烈推荐林锐博士这本《高质量C++/C 编程指南》,请花一两个小时认真阅读这本百页经书,你将会获益匪浅。草草看过,个人收获记录如下。

头文件的作用略作解释:

(1)通过头文件来调用库功能。在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。编译器会从库中提取相应的代码。

(2)头文件能加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。

另:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。

空行

a.在每个类声明之后、每个函数定义结束之后都要加空行。

b.在一个函数体内,逻揖上密切相关的语句之间不加空行,其它地方应加空行分隔。(这一条值得注意)

代码行

a.一行代码只做一件事情,如只定义一个变量,或只写一条语句。这样的代码容易阅读,并且方便于写注释。

b.if、for、while、do 等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加{}。这样可以防止书写失误。

for(initalization;contition;update)

{
    dosomething();
}
//空行
other();

c.尽可能在定义变量的同时初始化该变量(就近原则)

代码行内的空格

a.关键字后要留空格,如const、virtual、inline、case以及if、for、while;函数名之后不要留空格,紧跟左括号‘(’,以与关键字区别。

b.逗号与分号之后要留空格;“=”、“+=” “>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后应当加空格一元操作符前后不加空格

c.象“[]”、“.”、“->”这类操作符前后不加空格。

d.对于表达式比较长的for 语句和if 语句,为了紧凑起见可以适当地去掉一些空格。

for (i=0; i<10; i++) // 良好的风格

长行拆分

代码行最大长度宜控制在70 至80 个字符以内。长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。

if ((very_longer_variable1 >= very_longer_variable12)
    && (very_longer_variable3 <= very_longer_variable14)
    && (very_longer_variable5 <= very_longer_variable16))
{
    dosomething();
}

注释

a.注释是对代码的“提示”,而不是文档。程序中的注释不可喧宾夺主,注释太多了会让人眼花缭乱。注释的花样要少。

b.边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。

c.尽量避免在注释中使用缩写,特别是不常用缩写。

d.注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不可放在下方。

f.当代码比较长,特别是有多重嵌套时,应当在一些段落的结束处加注释,便于阅读。

类的版式

类的版式主要有两种方式:

(1)将private 类型的数据写在前面,而将public 类型的函数写在后面。采用这种版式的程序员主张类的设计“以数据为中心”,重点关注类的内部结构。

(2)将public 类型的函数写在前面,而将private 类型的数据写在后面。采用这种版式的程序员主张类的设计“以行为为中心”,重点关注的是类应该提供什么样的接口(或服务)。

建议读者采用“以行为为中心”的书写方式,即首先考虑类应该提供什么样的函数。

class A
{
    public:
        void Func1(void);
        void Func2(void);
        …
    private:
        int i, j;
        float x, y;
        …
}

共性规则

a.标识符应当直观且可以拼读,可望文知意;标识符的长度应当符合“min-length && max-information”原则。

b.命名规则尽量与所采用的操作系统或开发工具的风格保持一致。

例如 Windows 应用程序的标识符通常采用“大小写”混排的方式,如AddChild。而Unix 应用程序的标识符通常采用“小写加下划线”的方式,如add_child。别把这两类风格混在一起使用。

c.变量的名字应当使用“名词”或者“形容词+名词”;全局函数的名字应当使用“动词”或者“动词+名词”(动宾词组)。

d.尽量避免名字中出现数字编号,如Value1,Value2 等,除非逻辑上的确需要编号。这是为了防止程序员偷懒,不肯为命名动脑筋而导致产生无意义的名字(因为用数字编号最省事)。

简单的Windows 应用程序命名规则

a.类名和函数名用大写字母开头的单词组合而成

b.变量和参数用小写字母开头的单词组合而成。

c.常量全用大写的字母,用下划线分割单词。

d.静态变量加前缀s_(表示static)。

e.如果不得已需要全局变量,则使全局变量加前缀g_(表示global)。

f.类的数据成员加前缀m_(表示member),这样可以避免数据成员与成员函数的参数同名。

g.为了防止某一软件库中的一些标识符和其它软件库中的冲突,可以为各种标识符加上能反映软件性质的前缀。

if语句

不可将浮点变量用“==”或“!=”与任何数字比较。

if ((x >= -EPSINON) && (x <= EPSINON))
//其中EPSINON 是允许的误差(即精度)。

有时候我们可能会看到 if (NULL == p) 这样古怪的格式。不是程序写错了,是程序员为了防止将 if (p == NULL) 误写成 if (p = NULL),而有意把p 和NULL 颠倒。编译器认为 if (p = NULL) 是合法的,但是会指出 if (NULL = p)是错误的,因为NULL不能被赋值。

程序中有时会遇到 if/else/return 的组合,应该将如下不良风格的程序

if (condition)
    return x;
return y;

改写为

if (condition)
{
    return x;
}
else
{
    return y;
}

或者改写成更加简练的

return (condition ? x : y);

循环语句的效率

C++/C 循环语句中,for 语句使用频率最高,while 语句其次,do 语句很少用。提高循环体效率的基本办法是降低循环体的复杂性。

在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU 跨切循环层的次数。

switch 语句

不要忘记最后那个default 分支。即使程序真的不需要default 处理,也应该保留语句 default : break; 这样做并非多此一举,而是为了防止别人误以为你忘了default 处理。

goto 语句

我们主张少用、慎用goto 语句,而不是禁用。

const 与 #define 的比较

C++ 语言可以用const 来定义常量,也可以用 #define 来定义常量。但是前者比后者有更多的优点:

(1)const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。

(2)有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

建议:在C++ 程序中只使用const 常量而不使用宏常量。

类中的常量

有时我们希望某些常量只在类中有效。由于#define 定义的宏常量是全局的,不能达到目的,于是想当然地觉得应该用const 修饰数据成员来实现。const 数据成员的确是存在的,但其含义却不是我们所期望的。const 数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,因为类可以创建多个对象,不同的对象其const 数据成员的值可以不同。不能在类声明中初始化const 数据成员。以下用法是错误的,因为类的对象未被创建时,编译器不知道SIZE 的值是什么。

class A
{
    …
    const int SIZE = 100; // 错误,企图在类声明中初始化const 数据成员
    int array[SIZE]; // 错误,未知的SIZE
};

const 数据成员的初始化只能在类构造函数的初始化表中进行,例如

class A
{
    …
    A(int size); // 构造函数
    const int SIZE ;
};
A::A(int size) : SIZE(size) // 构造函数的初始化表
{
    …
}
A a(100); // 对象 a 的SIZE 值为100
A b(200); // 对象 b 的SIZE 值为200

怎样才能建立在整个类中都恒定的常量呢?别指望const 数据成员了,应该用类中的枚举常量来实现。例如

class A
{
    …
    enum { SIZE1 = 100, SIZE2 = 200}; // 枚举常量
    int array1[SIZE1];
    int array2[SIZE2];
};

枚举常量不会占用对象的存储空间,它们在编译时被全部求值。枚举常量的缺点是:它的隐含数据类型是整数,其最大值有限,且不能表示浮点数(如PI=3.14159)。
原文链接: https://www.cnblogs.com/houkai/archive/2013/05/31/3110599.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/90659

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月10日 上午12:46
下一篇 2023年2月10日 上午12:48

相关推荐