前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++ 作用域使用规范建议

C++ 作用域使用规范建议

作者头像
恋喵大鲤鱼
发布2022-05-09 11:01:15
1K0
发布2022-05-09 11:01:15
举报
文章被收录于专栏:C/C++基础C/C++基础

1.名字空间(Namespace)

C++ 在 C 的基础上引入了名字空间机制,使C中作用域的级别从原有的文件域(全局作用域)、函数作用域和代码块作用域(局部域)增加了名字空间域和类域。名字空间是ANSI C++引入的可以由用户命名的作用域,用来处理程序中常见的同名冲突。

**优点:**命名空间提供了(可嵌套)命名轴线(name axis,注:将命名分割在丌同命名空间内),当然,类也提供了(可嵌套)的命名轴线(注:将命名分割在丌同类的作用域内)。

**缺点:**命名空间具有迷惑性,因为它们和类一样提供了额外的(可嵌套的)命名轴线。在头文件中使用不具名的空间(匿名名字空间)容易违背C++的唯一定义原则(One Definition Rule (ODR))。

使用名字空间应该坚持以下几点规范: (1)推荐和提倡使用匿名名字空间

代码语言:javascript
复制
// .cpp文件中 
namespace 
{ 
	// 命名空间的内容无需缩进
	enum { UNUSED, EOF, ERROR }; // 经常使用的符号 
	bool AtEof() { return pos_ == EOF; } // 使用本命名空间内的符号EOF 
} // namespace 

匿名名字空间结束时用注释// namespace标识。

使用匿名名字空间的作用主要是将匿名名字空间中的成员的作用域限制在源文件中,其作用域与使用static关键字类似,但是与static关键字不同的是:包含在匿名名字空间中的成员(变量或者函数)具有外部连接特性,而用static修饰的变量或者函数具有内部连接特性,不能用来实例化模板的非类型参数。参考如下代码:

代码语言:javascript
复制
#include <iostream>
using namespace std;

template <char*p> class Example 
{
public:
    void display() 
    {
        cout<<*p<<endl;
    }
};

static  char c='a';
int main(int argc,char* argv[])
{
	Example<&c> a; //编译出错
    a.display();
}

此程序无法通过编译,因为静态变量c不具有外部连接特性,因此不是真正的“全局”变量。而类模板的非类型参数要求是编译时常量表达式,或者是指针类型的参数要求指针指向的对象具有外部连接性。同样是上面的这个程序,将char c=’a’;至于匿名空间进行定义,即可通过编译并运行。读者可自行考证。

(2)最好不要使用using指示符来引用名字空间 使用using指示符实际上就是取消了名字空间的保护作用,增加了命名冲突的概率。考察如下程序:

代码语言:javascript
复制
#include <iostream>
using std::cout;
using std::endl;

namespace FOO
{
	int a=1;
}

int a=2;

int main()
{
	using namespace  FOO;//引入了与a同名的变量
	cout<<"a:"<<a<<endl; //出现二义性
	return 0;
}

以上程序编译不通过,原因是使用using导入名字空间后,引入了名字空间中所有的成员,间接的取消了名字空间的保护作用,增加了同名标识符的命名冲突。如果要访问名字空间FOO中的变量a的话,真确的用法应该是使用作用域运算符::来指明a所在的作用域,即cout<<FOO::a<<endl;

(3)尽量不要使用全局函数 应该使用命名空间中的非成员函数和类的静态成员函数。这样做的原因是在某些情况下,非成员函数和静态成员函数是非常有用的,将非成员函数置于命名空间中可避免对全尿作用域的污染。

有时,不把函数限定在类的实体中是有益的,甚至需要这么做,要么作为静态成员,要么作为非成员函数。非成员函数不应依赖于外部变量,并尽量置于某个名字空间中。相比单纯为了封装若干不共享任何静态数据的静态成员函数而创建类,不如使用名字空间。

定义于同一编译单元的函数,被其他编译单元直接调用可能会引入不必要的连接依赖,静态成员函数对此尤其敏感。可以考虑提取到新类中,或者将函数置于独立的名字空间中。如果你确实需要定义非成员函数,又只是在.cpp文件中使用它,可使用匿名名字空间或static(如static int Foo() {…})限定其作用域。

2.嵌套类(Nested Class)

在一个类体中定义的类叫作嵌套类,也叫成员类(member class)。拥有嵌套类的类叫外围类,有些地方也叫被嵌套类。

代码语言:javascript
复制
class Foo
{ 
private: 
	//Bar是嵌套在Foo中的成员类 
	class Bar
	{ 
		... 
	}; 
};

优点:当嵌套(成员)类只在被嵌套类(enclosing class)中使用时很有用,将其置于被嵌套类作用域作为被嵌套类的成员不会污染其他作用域同名类。可在被嵌套类中前置声明嵌套类,在.cpp文件中定义嵌套类,避免在被嵌套类中包含嵌套类的定义,因为嵌套类的定义通常只与实现相关。

缺点:只能在被嵌套类的定义中才能前置声明嵌套类。因此,任何使用Foo::Bar*值针的头文件必须包含整个Foo的声明。

规范:不要将嵌套类定义为public,除非它们是接口的一部分,比如,某方法使用了这个类的一系列选项。

3.局部变量(Local Variable)

(1)将局部变量尽可能置于最小作用域内,在定义时将其显示初始化 C++允许在函数的任何位置声明和定义变量,我们提倡在尽可能小的作用域中定义变量,离第一次使用的位置越近越好,使得代码易于阅读、易于定位变量的定义位置、变量类型和初始值。特别的,在定义变量时应显示的初始化。

代码语言:javascript
复制
int i; 
i = f(); 		// 坏——初始化和声明分离 
int i = g(); 	// 好——初始化时声明

(2)构造数据类型的变量尽可能放在循环体外定义 如果变量是一个对象,每次进入作用域都要调用其构造函数,每次退出作用域都要调用其析构函数。

代码语言:javascript
复制
 // 低效的实现
 for (int i = 0; i < 1000000; ++i)
 {
	 Foo f; // 构造函数和析构函数分别调用1000000次! 
	 f.DoSomething(i);
}

类似以下变量f放到循环作用域外面声明要高效的多:

代码语言:javascript
复制
Foo f; //构造函数和析极函数只调用1次
for(int i = 0; i < 1000000; ++i)
{
	f.DoSomething(i); 
}

4.全局变量(Global Variables)

(1)尽量不要定义构造类型的全局变量 构造类型的全局变量,如类对象的构造函数、析构函数以及初始化操作的调用顺序只是被部分规定,每次生成有可能会有发化,从而导致难以发现的bugs。

因此,应禁止使用class类型的全局变量(包括STL的string, vector等等),因为它们的初始化顺序有可能导致构造出现问题。内建类型和由内建类型构成的没有构造函数的结构体可以使用,如果一定要使用class类型的全局变量,请使用单件模式(singleton pattern)。

(2)对于全局的字符串常量,使用C风格的字符串,而不要使用STL的字符串

代码语言:javascript
复制
const char kFrogSays[] = "ribbet";

虽然允许在全局作用域中使用全局发量,使用时务必三思。大多数全局变量应该是类的静态数据成员,或者当其只在.cpp文件中使用时,将其定义到不具名名字空间中,或者使用静态关联以限制变量的作用域。

记住,静态成员变量视为作用域限制在类域的全局变量,所以,也不能是class类型!

5.小结

(1)cpp源文件中的匿名名字空间可避免命名冲突、限定作用域,避免直接使用using指示符污染命名空间; (2)嵌套类符合局部使用原则,只是不能在其他头文件中前置定义,尽量不要设为public; (3)尽量不用全局函数和全局变量,考虑作用域和命名空间限制,尽量单独形成编译单元; (4)多线程中的全局变量(含静态成员变量)不要使用class类型(含STL容器),避免不明确行为导致的bugs。

作用域的使用,除了考虑名称污染、可读性之外,主要是为降低耦合度、提高编译和执行效率。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-01-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.名字空间(Namespace)
  • 2.嵌套类(Nested Class)
  • 3.局部变量(Local Variable)
  • 4.全局变量(Global Variables)
  • 5.小结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档