作用域
命名空间
命名空间里的内容不缩进。
建议 6.1.1 对于 cpp 文件中不需要导出的变量,常量或者函数,请使用匿名 namespace 封装或者用 static 修饰
在 C++ 2003 标准规范中,使用 static 修饰文件作用域的变量,函数等被标记为 deprecated 特性,所以更推荐使用匿名 namespace。
主要原因如下:
- static 在 C++中已经赋予了太多的含义,静态函数成员变量,静态成员函数,静态全局变量,静态函数局部变量,每一种都有特殊的处理。
- static 只能保证变量,常量和函数的文件作用域,但是 namespace 还可以封装类型等。
- 统一 namespace 来处理 C++的作用域,而不需要同时使用 static 和 namespace 来管理。
- static 修饰的函数不能用来实例化模板,而匿名 namespace 可以。但是不要在 .h 中使用中使用匿名 namespace 或者 static。
// Foo.cpp
namespace {
const int kMaxCount = 20;
void InternalFun(){};
}
void Foo::Fun() {
int i = kMaxCount;
InternalFun();
}
规则 6.1.1 不要在头文件中或者#include 之前使用 using 导入命名空间
说明:使用 using 导入命名空间会影响后续代码,易造成符号冲突,所以不要在头文件以及源文件中的#include 之前使用 using 导入命名空间。示例:
// 头文件a.h
namespace namespacea {
int Fun(int);
}
// 头文件b.h
namespace namespaceb {
int Fun(int);
}
using namespace namespaceb;
void G() {
Fun(1);
}
// 源代码a.cpp
#include "a.h"
using namespace namespacea;
#include "b.h"
void main() {
G(); // using namespace namespacea在#include “b.h”之前,引发歧义:namespacea::Fun,namespaceb::Fun调用不明确
}
对于在头文件中使用 using 导入单个符号或定义别名,允许在模块自定义名字空间中使用,但禁止在全局名字空间中使用。
// foo.h
#include <fancy/string>
using fancy::string; // Bad,禁止向全局名字空间导入符号
namespace foo {
using fancy::string; // Good,可以在模块自定义名字空间中导入符号
using MyVector = fancy::vector<int>; // Good,C++11可在自定义名字空间中定义别名
}
全局函数和静态成员函数
建议 6.2.1 优先使用命名空间来管理全局函数,如果和某个 class 有直接关系的,可以使用静态成员函数
说明:非成员函数放在名字空间内可避免污染全局作用域, 也不要用类+静态成员方法来简单管理全局函数。 如果某个全局函数和某个类有紧密联系, 那么可以作为类的静态成员函数。
如果你需要定义一些全局函数,给某个 cpp 文件使用,那么请使用匿名 namespace 来管理。
namespace mynamespace {
int Add(int a, int b);
}
class File {
public:
static File CreateTempFile(const std::string& fileName);
};
全局常量和静态成员常量
建议 6.3.1 优先使用命名空间来管理全局常量,如果和某个 class 有直接关系的,可以使用静态成员常量
说明:全局常量放在命名空间内可避免污染全局作用域, 也不要用类+静态成员常量来简单管理全局常量。 如果某个全局常量和某个类有紧密联系, 那么可以作为类的静态成员常量。
如果你需要定义一些全局常量,只给某个 cpp 文件使用,那么请使用匿名 namespace 来管理。
namespace mynamespace {
const int kMaxSize = 100;
}
class File {
public:
static const std::string kName;
};
全局变量
建议 6.4.1 尽量避免使用全局变量,考虑使用单例模式
说明:全局变量是可以修改和读取的,那么这样会导致业务代码和这个全局变量产生数据耦合。
int counter = 0;
// a.cpp
counter++;
// b.cpp
counter++;
// c.cpp
cout << counter << endl;
使用单实例模式
class Counter {
public:
static Counter& GetInstance() {
static Counter counter;
return counter;
} // 单实例实现简单举例
void Increase() {
value++;
}
void Print() const {
std::cout << value << std::endl;
}
private:
Counter() : value(0) {}
private:
int value;
};
// a.cpp
Counter::GetInstance().Increase();
// b.cpp
Counter::GetInstance().Increase();
// c.cpp
Counter::GetInstance().Print();
实现单例模式以后,实现了全局唯一一个实例,和全局变量同样的效果,并且单实例提供了更好的封装性。
例外:有的时候全局变量的作用域仅仅是模块内部,这样进程空间里面就会有多个全局变量实例,每个模块持有一份,这种场景下是无法使用单例模式解决的。