大规模C++程序设计基本设计规则

1.保持数据成员私有。

2.避免在文件作用域内包含带有外部链接的数据。
文件作用域中带有外部链接的数据,与存在于其他编译单元中的全局变量有冲突的危险。全局变量将对象和代码绑在一起,使得在其他程序中几乎不能选择性地重用编译单元。

简单将这些变量非全局化:
(1) 将所有全局变量放入一个结构中;
(2) 然后将他们私有化并添加静态访问函数。

假设我们有以下全局变量:

int size;
double scale;
const char * system;

通过这些变量放入一个结构内并使之成为该结构的静态成员,就可以把他们从全局名字空间中删除:

struct Global{
    static int s_size;   //bad idea(public data)
    static double s_scale;  //bad idea(public data)
    static const char *s_system;  //bad idea(public data)
};

现在不使用size,scale或system访问全局变量,应该使用Global::s_size、Global::s_scale 或者 Global::s_system 访问全局变量。

对于上面,我们是直接访问了静态成员数据,如果我们要把一个成员(如s_size)的导出数据类型从int变为double,则会改变接口,对所有的客户端程序都会收到影响。若通过静态成员访问(和操纵)静态成员,则允许这种局部修改,不会扰乱全局作用域。

故可以建立一个Global类并为其添加静态操纵函数和访问函数方法来删除公共数据。由于所有接口函数都是静态的,所以没有必要实例化一个对象。如下直接 Global::setsize(2) 既可以修改Global中的s_size值。

class Global
{
    static int s_size;
    static double s_scale;
    static const char *s_system;
private:
    Global(); 
public:
    static void setSize(int size) {s_size=size}
    static void setScale(double scale) {s_scale=scale}
    static void setSystem(const char *system){s_system=system;}
}

3.避免在.h文件的文件作用域中使用自由函数(运算符函数除外); 在.c 文件中避免使用带有外部链接的自由函数(包括运算符函数)
解决方法为 将自由函数分组到一个只包含静态函数的工具类(结构体)。

4.避免在 .h 文件作用域内使用枚举、typedef 和 常量数据。
尽管3种类型都是内部链接,尽管C++支持嵌套,在一个类的作用域内定义的枚举不会和全局名字空间中的其他名字冲突。通过选择在一个更有限的作用于内定义一个枚举,可以确保枚举类型的所有枚举成员作用域都类似,并不会在与作用域之外定义的其他名字冲突。

//paint.h
enum Color {RED,GREEN,BLUE,ORANGE,YELLOW};

//juice.h
enum Fruit {APPLE,ORANGE,GRAPE};

//picture.c

#include "picture.h"
#include "paint.h"
#include "juice.h"

这个俩个枚举可能不是同个开发者写的,但是某一天在同个文件夹,出现俩个 ORANGE, 会报错。

若在俩个类中定义这俩个枚举,则有 Paint::Orange 或者 Juice::Orange , 方便的消除了二义性。

因此,typedef 和 常量数据应该放在头文件的类作用域内。如下所示:

//array.h
#ifndef INCLUDE_ARRAY
#define INCLUDE_ARRAY

class String;
class Array{
    enum {DEFAULT_SIZE=100};
    static const double DEFAULT_VALUE;
    static const String DEFAULT_VALUE;
}
#endif 
//array.c
#include "array.h"
#include "str.h"

double Arrary::DEFAULT_VALUE=0.0;
String Arrary::DEFAULT_NAME="";

//...

5.除非作为包含guard(卫哨),否则应该避免在头文件中使用预处理宏。
举个容易范的例子:

//theircode.h
#ifndef INCLUDE_THEIRCODE
#define INCLUDE_THEIRCODE
//...
#define GOOD 0; //bad idea
//...
#endif
//theircode.c
#include "ourcode.h"
#include "theircode.h"
//...
int ourClass::aFunction()
{
    enum{BAD=-1,GOOD=0} status=GOOD;
    //...
    return status;
};
//...

则直接变为:

//theircode.c
#include "ourcode.h"
#include "theircode.h"
//...
int ourClass::aFunction()
{
    enum{BAD=-1,0=0} status=0;
    //...
    return status;
};
//...

然后就报错了。

6.在一个.h文件作用域中只应该声明类、结构体、联合体和自由运算符函数; 在.h文件作用域中只应该定义类、结构体、联合体和内联(成员或自由运算符)函数。
这里写图片描述
这里写图片描述

7.在每一个头文件的内容周围放置一个唯一且可预知的(内部)包含卫哨(guard)。

//a.h
#ifndef INCLUDED_A  //这就是包含卫哨
#define INCLUDED_A  
...
#endif

在一个编译单元中包含 a.h 时,预处理器会首先检查预处理器符号INCLUDED_A是否已被定义。如果没有定义,卫哨(guard) 符号INCLUDED_A将被定义一次但用于全部(针对这个编译单元),然后通过读包含在头文件剩余部分的定义继续进行预处理。

这个头文件被第二次包含的时候,预处理器#ifndef 条件从句中的内容将被忽略。

次要规则:在每个头文件的预处理器包含指示符周围放置冗余的(外部的)包含卫哨,具体如下:

//b.h
#ifndef INCLUDED_B
#define INCLUDED_B
...
#endif
//a.h
#ifndef INCLUDED_A  //这就是包含卫哨
#define INCLUDED_A  

#ifndef INCLUDE_B  //这就是冗余包含卫哨,多用于界面控件
#include "b.h"
#endif  

#endif

8.实现代码时,使用assert语句有助于对假设条件编写文档。
有些软件开发者认为处理进入一个函数的每个指针是必要的,即使那个指针是空的。如果这个函数是一种广泛使用的接口的一部分,完全可以证明这是一个支持鲁棒性的正确决定。在函数实现的开头用assert语句判断指针是否为空。

//stdio.c
#include <stdio.h>
#include <assert.h>

int printf(const char *format...)
{
    assert(format);  //判断下指针是否为空。
    /* ..
    */
}

9.命名规则可参考如下:

1.类标识名以大写字母开头。
2.函数和数据以小写字母开头。
3.多词标识名的第二个及以后词的首字母大写。
4.常量和宏指令都用大写字母(用下划线分隔单词)。
5.类数据成以d_为前缀,静态成员以s_为前缀。
6.类成员函数将按照创建、操纵和访问分类来组织。
7.在类定义私有细节放在公共接口前(主要是为了强调它们在头文件中的存在)。

参考:大规模C++ 程序设计/(美) 洛科什 (Lakos. J.)著; 刘冰,张林译. —北京: 机械工业出版社,2014.8

猜你喜欢

转载自blog.csdn.net/Hansry/article/details/80210784