《Google C++编码规范》读书笔记第二章:作用域

《Google C++编码规范》第二章、作用域

一.命名空间(namespace)

定义:命名空间将全局作用域分成不同的、具名的作用域。
优点:命名空间可有效防止全局作用的命名冲突,例如两个命名空间pro1::Foopro2::Foo就不会冲突。
缺点:命名空间具有迷惑性,在头文件中使用不具名的空间容易违背C++的唯一定义原则(One Definition Rule, ODR)(在此有疑惑)。
1. 不具名命名空间(Unnamed Namespaces)
.cc/.cpp源文件中允许提倡使用不具名命名空间,可以避免命名冲突,例如:

namespace {
//内容不缩进
...
}   //namespace

注意,不能在头文件中使用不具名空间

  1. 具名命名空间(Named Namespaces)
    具名命名空间将源文件封装起来,除了文件包含、全局变量的声明/定义、类的前置声明。例如:
//.h
namespace mynamespace {
class MyClass {
public:
    ...
    void Foo();
};

}   //mynamespace
//.cc
namespace mynamespace {
void MyClass::Foo() {
    ...
}

}   //mynamespace

源文件会包含更多细节,包括对其它命名空间中的类的引用等。

#include "a.h"
class C;    //全局命名空间类的前置声明
namespace a { class A; }    //命名空间a中的a::A的前置声明
namespace b {
...
}   //b

不要声明命名空间std下的任何内容,包括标准类库的前置声明。声明std下的实体会导致未定义的行为。
最好不要使用using指示符,保证该命名空间下的所有名称都可以正常使用。

//禁止--污染命名空间
using namespace foo;
//那常见的
//using namespace std; 

但是可以使用using指示符:

//可以在源文件和头文件中使用
//注意,在头文件中使用必须在函数、方法和类的内部使用
using ::foo::bar;
using std::string;
using std::cout;

在源文件和头文件(函数、方法和类的内部)中,可以使用命名空间别名

namespace fbz = ::foo::bar::baz;

二.嵌套类(Nested Class)

将嵌套类声明置于命名空间是比较好的选择。
定义:可以在一个类中定义另外一个类,嵌套类也称为成员类(member class)。

class Foo { //被嵌套类Foo
private:
    //Bar是嵌套在Foo中的成员类
    class Bar {
        ...
    };
};

优点:不会污染其它作用类的同名类,在被嵌套类中前置声明嵌套类,在源文件中定义嵌套类,要注意避免在被嵌套类中包含嵌套类的定义
缺点:只能在被嵌套类的定义中才能前置声明嵌套类,因此,任何使用Foo::Bar*指针的头文件必须包含整个Foo的声明。
结论:不要将嵌套类定义为public,除非它们是接口的一部分。

三.非成员函数,静态成员函数,全局函数

使用命名空间的非成员函数或者静态成员函数,尽量不要使用全局函数。
优点:将非成员函数置于命名空间可以避免对全局作用域的污染。
不把函数限定在类的实体中是有益的,要么作为静态成员,要么作为非成员函数。非成员函数不应该依赖于外部变量,并尽量置于某个命名空间中。单纯为了封装而不共享任何静态数据成员的静态成员函数而创建类,不如使用命名空间。
如果确是需要定义非成员函数,只是在.cc文件中使用它,可使用不具名命名空间或者static关联限定作用域。

四.局部变量(local variables)

声明变量时就将其初始化。提倡在最小的作用域中声明变量,离第一次使用越近越好,使用初始化式来代替声明+赋值的方式

int i;
i = Foo();
int j = Foo();  //这是提倡的

再循环体中,如果变量是一个对象,每次进入作用域都要调用其构造函数,每次退出都要调用析构函数。

for(int i = 0; i < 10000000; ++i) {
    Foo f;  //低效的做法,构造函数和析构函数都要调用10000000...
}

//更加高效的做法是在循环作用域外就声明
Foo f;  //构造函数和析构函数只调用一次
for(int i = 0; i < 10000000; ++i) {
    ...
}

五.全局变量(global variables)

class类型的全局变量是被禁止的,内建类型的全局变量是允许的,多线程中的多数全局变量也是被禁止的。永远不要使用函数返回值来初始化全局变量。
全局变量的构造函数,析构函数以及初始化顺序都是被部分规定,详见我的这篇博客执行器语意学
禁止使用class类型的全局变量(例如STL的string,vector等等),因为初始化顺序可能导致构造出现问题。内建类型和没有构造函数的结构体可以使用,如果一定要使用class类型的全局变量,请使用单例模式(Singleton pattern).
对于全局的字符串常量,请使用C风格的字符串,而不要使用STL的字符串:

const char str[] = "ribbet";

大多数全局变量应该是类的静态数据成员,记住,静态成员变量视为全局变量,所以也不能是class 类型。

总结:
- .cc中不具名命名空间可以避免命名冲突、限定作用域,避免直接使用using提示符污染命名空间。
- 嵌套类符合局部变量使用原则,只是不能再其它头文件中前置声明,尽量不要public。
- 尽量不要使用全局函数和全局变量,考虑作用域和命名空间限制,尽量单独形成编译单元。
- 多线程的全局变量(静态成员变量)不要使用class类型,避免不明确的行为导致bugs。
- 作用域的使用,考虑命名污染、可读性之外,主要是降低耦合、提高编译、执行效率。

主要参考自《Google C++编码规范》中文版

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值