C++ namespace 理解与操作详解

学习C++的过程中遇到了命名空间的问题,之前对这个问题一直是迷迷糊糊的不是很清楚,现在来把这块的知识点理清楚,总结如下分享给大家。

using namespace std;

(1).首先这句话是什么?

单个成分:

using ,namespace是C++中的关键字,而std是C++标准库所在空间的名称

namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace的空间中。

如果想使用Boost的库,那么将std换为Boost就可以了

这句话整体的意思就是暴露std这个名称空间,让我们可以调用std这个名字空间下的东西,可以认为是获得一种权限,就像是使用库函数前要包含头文件一样,命名空间它可作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。

(2)它有什么用?

C++语言提供一个全局的命名空间namespace,可以避免导致全局命名冲突问题

所谓命名空间,是一种将程序库名称封装起来的方法,它就像在各个程序库中立起一道道围墙,或者说指定了很多同名的孩子(变量)的归属,因为随着C++的不断发展,关键字和变量越来越多,在工程中使用有遇到重命名的问题越来越多,所以引入了命名空间,让每一个即使是同名的变量都可以辨识。

(3).它的用法:

三种用法:

就拿sdm 和temp来举例子吧(sdm是temp库中的一个变量)

1. using namespace sdm;

然后就可以使用变量temp了: 同时,还可以使用sdm中的任何一个其他成员了

(但是这种方式有些危险,是一个不好的副作用)

2.using sdm::temp; 只声明temp, 在这个文件中只能使用sdm中的temp这个变量

3.不声明,而是在直接使用的时候使用sdm::temp

(4)

声明整个命名空间时,需要使用namespace的关键字,

格式为: namespace namespace_name {

// 代码声明

}

需要使用命名空间中的函数或者变量时,就在前面加上命名空间

如: name: :code

而引入单个变量或者函数就不用namespace 而是直接使用对应的空间名,如 using std: :cout;

注意点:

命名空间是可以镶嵌定义的,下面用一段代码演示:镶嵌定义和命名空间的使用

#include<string.h>
#include<iostream>
#include<stdio.h>

namespace tong
{
	char name[10] = "李四";
	int year = 18;
	namespace count{
		int year = 19;
		char name[10] = "永远爱你";	}
}

int main()
{
	char name[10] = "张三";
	int year = 28;

	printf("name=%s\n",name);
	printf("song::name=%s\n", tong::name);
	printf("year = %d\n",year );
	printf("song::year=%d\n", tong::year);
	printf("song::count::name = %s\n", tong::count::name);
	printf("song::count::yearr=%d", tong::count::year);
	return 0;
}   结果如下
name=张三
song: :name=李四
year=28
song: :year=18
song: : count: name =永远爱你
song: : count: : yearr=19

5)命名空间使用中最大的问题:

命名空间作用范围冲突的问题:

举个简单的例子:命名空间的声明和头文件如上,我们尝试着改变using namespace tong;的位置来探索它发挥的作用。

int main()
{
	char name[10] = "张三";
	int year = 28;
	printf("name=%s\n", name);
	printf("year = %d\n", year);
	using namespace tong;

	//printf("");
	printf("name=%s\n",name);
	printf("year = %d\n",year );
}

第一个例子:将using namespace tong;放在main函数内部,或者放在main函数前面,打印的结果都不会改变,都是定义在main函数内部的“张三”和“year”,而不是前面定义在tong空间内的变量,如果要打印tong内部的变量就只能使用::(作用域限定符),这说明了:在一个代码块内如果有和命名空间相同的变量名,那么使用using namespace tong;是无效的。

为了验证上面结论我们将原来main中的两个变量定义在全局,再来看看效果。

我们会发现会报错“name,year不明确”,但是前两个printf的参数是没有问题的,而这也就是我们在使用命名空间时最棘手的问题,下面先来介绍些别的知识,然后会对我们理解上述问题有帮助。

using namespace指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。

using指令引入的名称遵循正常的范围规则。名称从使用using指令开始是可见的,直到该范围结束。此时,在范围以外定义的同名实体是隐藏的。那么该范围结束的标志是什么呢?就是下一个命名空间被声明开始起作用的时候,上一个命名空间作用自动结束,但是对于全局变量无效。

下面引用一位菜鸟上的大佬的演示,我们会理解的很清楚:

#include <iostream>

using namespace std;
namespace A {
    int a = 100;
    namespace B            //嵌套一个命名空间B
    {
        int a = 20;
    }
}

int a = 200;//定义一个全局变量


int main(int argc, char *argv[]) {
    cout << "A::a =" << A::a << endl;        //A::a =100
    cout << "A::B::a =" << A::B::a << endl;  //A::B::a =20
    cout << "a =" << a << endl;              //a =200
    cout << "::a =" << ::a << endl;          //::a =200

    using namespace A;
    cout << "a =" << a << endl;     // Reference to 'a' is ambiguous // 命名空间冲突,编译期错误
    cout << "::a =" << ::a << endl; //::a =200

    int a = 30;
    cout << "a =" << a << endl;     //a =30
    cout << "::a =" << ::a << endl; //::a =200

    //即:全局变量 a 表达为 ::a,用于当有同名的局部变量时来区别两者。

    using namespace A;
    cout << "a =" << a << endl;     // a =30  // 当有本地同名变量后,优先使用本地,冲突解除
    cout << "::a =" << ::a << endl; //::a =200


    return 0;
}

(6)不连续的命名空间

命名空间可以定义在几个不同的部分中,因此命名空间是由几个单独定义的部分组成的。一个命名空间的各个组成部分可以分散在多个文件中。所以,如果命名空间中的某个组成部分需要请求定义在另一个文件中的名称,则仍然需要声明该名称。

具体来说是什么意思呢?就是说在这个文件里定义的命名空间只在这个文件内有效,除非你用头文件包含另一个文件,或者在这个文件中的命名空间中将你想使用的但已经被包含在另一个文件的命名空间的变量或者函数再声明一遍,

(7)<iostream>和<iostream.h>的渊源

<iostream>和<iostream.h>是不一样,前者没有后缀,实际上,在你的编译器include文件夹里面可以看到,二者是两个文件,打开文件就会发现,里面的代码是不一样的。

后缀为.h的头文件c++标准已经明确提出不支持了,早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,c++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。

因 此,当使用<iostream.h>时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现;当使 用<iostream>的时候,该头文件没有定义全局命名空间,必须使用namespace std;这样才能正确使用cout。

(7)using指令与using声明的比较



using编译指令和using声明,都可以简化对命名空间中名称的访问。

using指令使用后,可以一劳永逸,对整个命名空间的所有成员都有效,非常方便。而using声明,则必须对命名空间的不同成员名称,一个一个地去声明,非常麻烦。

但是,一般来说,使用using声明会更安全。因为,using声明只导入指定的名称,如果该名称与局部名称发生冲突,编译器会报错。而using指令导入整个命名空间中的所有成员的名称,包括那些可能根本用不到的名称,如果其中有名称与局部名称发生冲突,则编译器并不会发出任何警告信息,而只是用局部名去自动覆盖命名空间中的同名成员。特别是命名空间的开放性,使得一个命名空间的成员,可能分散在多个地方,程序员难以准确知道,别人到底为该命名空间添加了哪些名称。

虽然使用命名空间的方法,有多种可供选择。但是不能贪图方便,一味使用using 指令,这样就完全背离了设计命名空间的初衷,也失去了命名空间应该具有的防止名称冲突的功能。

一般情况下,对偶尔使用的命名空间成员,应该使用命名空间的作用域解析运算符来直接给名称定位。而对一个大命名空间中的经常要使用的少数几个成员,提倡使用using声明,而不应该使用using编译指令。只有需要反复使用同一个命名空间的许多数成员时,使用using编译指令,才被认为是可取的。

(8)无名命名空间

标准C++引入命名空间,除了可以避免成员的名称发生冲突之外,还可以使代码保持局部性,从而保护代码不被他人非法使用。如果你的目的主要是后者,而且又为替命名空间取一个好听、有意义、且与别人的命名空间不重名的名称而烦恼的话,标准C++还允许你定义一个无名命名空间。你可以在当前编译单元中(无名命名空间之外),直接使用无名命名空间中的成员名称,但是在当前编译单元之外,它又是不可见的。

无名命名空间的定义格式为:

namespace {
    

声明序列可选

}

实际上,上面的定义等价于:(标准C++中有一个隐含的使用指令)

namespace $$$ {
    

声明序列可选

}

using namespace $$$;

例如:

namespace {
   

int i;

void f() {/*……*/}

}

int main() {
   

i = 0; // 可直接使用无名命名空间中的成员i

f(); // 可直接使用无名命名空间中的成员f()

零散的知识点:

1.如果全局变量和局部变量相冲突,那么按就近原则来使用,但是在C++中使用::+变量名来表示全局变量,即使在有局部变量同名的时候

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值