C++编程规范 5 作用域、模板和C++其他特性

来源:互联网 发布:阿里云 代码托管 编辑:程序博客网 时间:2024/06/04 19:54

5 作用域、模板和C++其他特性

  5.1  作用域

原则5.1 使用名字空间进行归类,避免符号冲突

说明:名字空间主要解决符号冲突问题。

示例:两个不同项目的全局作用域都有一个类 Foo, 这样在编译或运行时造成冲突。如果每个项目将

代码置于不同名字空间中

    namespace  project1
    {
       class  Foo;
       //…
    }
    namespace  project2
    {
       class  Foo;
       //…
    }
作为不同符号自然不会冲突:project1::Foo 和 project2::Foo。

建议名字空间的名称采用全小写,为避免冲突,可采用项目(产品部件)或目录结构。

用名字空间把整个源文件封装起来, 以区别于其它名字空间,但是这些内容除外:文件包含(include),

全局标识的声明/定义以及类的前置声明。

示例:

    #include "a.h"
    DEFINE_bool(someflag, false, “dummy flag”);
   class C;                 //全局名字空间中类C的前置声明
    namespace a{class A;}    //a::A 的前置声明
    //以上代码就不要放在名字空间b中。
    namespace b
    {
        //b中的代码…
    } //namespace b

规则5.1 不要在头文件中或者#include之前使用using指示符

说明:使用using会影响后续代码,易造成符号冲突,所以不要在头文件以及源文件中的#include之前

使用using。

示例:

    //头文件a.h
    namespace A
    {
        int f(int);
    }
    //头文件b.h
    namespace B
    {
        int f(int);
    }
    using namespace B;
    void g()
    {
        f(1);
    }

    //源代码a.cpp
    #include “a.h”
    using namespace A;
    #include “b.h”
    void main()
    {
       g();//using namespace A在#include “b.h”之前,引发歧意:A::f,B::f调用不明确
    }

建议5.1 尽量少使用嵌套类

说明:一个类在另一个类中定义,这样的类被称为嵌套类。嵌套类是其外围类的成员,嵌套类也被称

为成员类。

    class Foo
    {
    private:
        //Bar是嵌套在Foo中的成员类
       class Bar
        {
           //…
        };
    };
跟其它类一样,嵌套类可以声明为public、priate和protected属性,因此,从外部访问嵌套类,遵循

类成员的访问规则。

一般来说,应该尽量少用嵌套类。嵌套类最适合用来对它们的外层类实现细节建模(如:方便实现链表、

容器等算法),且在这个类需要访问外围类所有成员(包括私有成员)时才使用嵌套类。 不要通过嵌套
类来进行命名分组,应该使用名字空间。

建议5.2 尽可能不使用局部类

说明:定义在函数体内的类称为局部类。局部类只在定义它的局部域内可见。

局部类的成员函数必须被定义在类定义中,且不能超过15行代码。否则,代码将变得很难理解。

建议5.3 使用静态成员函数或名字空间内的非成员函数,避免使用全局函数

说明:非成员函数放在名字空间内可避免污染全局作用域。

如果你必须定义非成员函数, 又只是在 .cpp 文件中使用它, 也可使用static关键字 (如 static int

Foo() {...}) 限定其作用域。

建议5.4 避免class类型的全局变量,尽量用单件模式

说明:静态生存周期的对象, 包括全局变量, 静态变量, 静态类成员变量, 以及函数静态变量, 都必

须是原生数据类型 (POD : Plain Old Data)。

静态变量的构造函数, 析构函数以及初始化操作的调用顺序在C++标准中未明确定义,从而导致难以发

现的 bug。比如作用域结束时,某个静态变量已经被析构了,但其他代码还试图访问该变量,导致系

统崩溃。所以只允许 POD 类型的静态变量。同样,不允许用函数返回值来初始化静态变量。

  5.2  模板

模板可以衍生出一系列的类和函数,这是一种形式的代码复用。但要注意,这种形式的复用是源代码级

而不是目标代码级的。也就是说,对模板的每一次实例化都会产生一份新的源代码。与此相反,继承允

许复用基类的大部分目标代码。

滥用模板会造成三个后果:第一,代码规模的过度膨胀;第二,当修改模板时,很难预料是否会对原先

正常工作的代码造成不良影响;第三,很难确保模板所有可能的合法实例化都能正常工作。所以模板的

使用要仔细且有节制。

建议5.5 谨慎使用模板,只使用模板的基础特性

说明:模板对编译器的要求很高,当前对模板100%支持的编译器几乎没有,因此,使用前应作测试,

特别是涉及偏特化,模板参数等高级特性时。

模板的错误提示比较难懂,产生的错误要映射回模板后才能显示给编程者看,但映射毕竟不是错代码

本身,所以有时很难看懂,有时甚至失真,对于人员素质要求也高,增加了维护难度。

建议5.6 注意使用模板的代码膨胀

说明:模板会为每个类型产生一个实例,如果使用不当,会产生过多实例,特别是根据常量实例化时。

建议5.7 模板类型应该使用引用或指针

说明:实例化和参数传递复杂类型(结构体,对象),传值的代价很高;引用和指针可以提高效率。

建议5.8 模板如果有约束条件,请在模板定义处显式说明

说明:编译对模板的检查较弱,很难保证检察所有错误,因此进行显式说明可以减少模板使用错误。
建议5.9 两个模块之间接口中尽量不要暴露模板

说明:因为在编译器优化选项打开的情况下,其编译的符号为不确定,导致依赖。

  5.3  其他

规则5.2 不要在extern "C"内部使用#include包含其他头文件

说明:在C++代码中调用C的库文件,需要用extern "C"来告诉编译器:这是一个用C写成的库文件,请

用C的方式来链接它们。

严格的讲,只应该把函数、变量以及函数类型这三种对象放置于extern "C"的内部。

如果把#include指令放在extern "C"里面,可能会导致extern "C"嵌套而带来如下几个风险:

extern "C"嵌套过深,导致编译错误

extern "C"嵌套后,改变其他某些文件的编译链接方式

建议5.10 避免使用友元

说明:友元扩大了 (但没有打破) 类的封装边界。友元会导致类间强耦合,打破封装,暴露出具体实

现,从而使友元和类的实现紧耦合;友元不可继承,降低可继承性。

例外:某些情况下, 相对于将类成员声明为public, 使用友元是更好的选择, 尤其是你只允许另一个

类访问该类的私有成员时。

建议5.11 避免使用RTTI

说明:RTTI允许在运行时识别对象的类型。使用RTTI很容易违反“开放封闭原则”。如果要根据派生

类的类型来确定执行不同逻辑代码, 虚函数无疑更合适,在对象内部就可以处理类型识别问题;如果

要在对象外部的代码中判断类型, 考虑使用双重分派方案, 如访问者模式,在对象本身之外确定类的

类型。所以,不建议使用RTTI,除非在一些特定场合如某些单元测试中会用到。

建议5.12 使用sizeof(变量)而不是sizeof(类型)

说明:使用 sizeof(varname),当代码中变量类型改变时会自动更新。

    struct DATA data;
    memset(&data, 0, sizeof(data));
    memset(&data, 0, sizeof(DATA));  //当data改为其他类型时,出错。
另外,对数组来说,sizeof(数组变量) 并不一定是元素的个数,元素个数是sizeof(数组变

量)/sizeof(数组变量[0])。

原创粉丝点击