个人主页:Jason_from_China-CSDN博客
所属栏目:C++系统性学习_Jason_from_China的博客-CSDN博客
namespace的价值
避免命名冲突:在大型项目或使用多个库的情况下,不同部分可能会定义相同名称的实体(如变量、函数、类等)。使用命名空间可以有效地避免这些命名冲突。【这一点的尤其关键的,在C语言里面,是没有域这样的区分之说的,所以会导致命名的冲突,尤其的当命名应该新变量的时候,如果引入新的库,会导致命名错误,就需要大量的修改命名,这会很麻烦。但是在C++里面,我们只需要在外层封装一层就可以了】
//这个C语言的代码就是有问题的,因为引入了新的库,从而导致需要更改命名, //这里还是比较少的代码,如果代码数量比较多,那么就会存在大量重复的代码的工作 #include <stdio.h> #include <stdio.h> #include <stdlib.h> int rand = 10; int main() { // 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数” printf("%d\n", rand); return 0; }
组织代码逻辑:命名空间可以将逻辑相关的代码组织在一起,使得代码结构更加清晰,便于理解和维护。
提供上下文:命名空间提供了一种上下文,使得程序员可以快速了解某个实体属于哪个模块或库。
支持大型项目开发:在大型项目中,可能需要包含多个库和子系统,命名空间的使用可以确保这些不同的部分在全局作用域中不会相互干扰。
促进代码重用:命名空间允许程序员在不同的上下文中重用相同的名称,而不必担心名称冲突,从而提高了代码的重用性。
简化代码编写:使用命名空间后,程序员可以在不同的命名空间中定义具有相同名称的变量或函数,简化了代码编写。
支持模块化开发:命名空间支持模块化开发,每个模块可以有自己的命名空间,从而实现代码的解耦。
提高编译效率:编译器在查找未限定名称时,会先在局部作用域查找,然后是类作用域,接着是命名空间作用域,最后是全局作用域。这种分层次的查找顺序可以提高编译效率。
便于代码维护:当需要修改或更新某个模块时,只需要关注该模块的命名空间,减少了对其他模块的影响。
支持语言的扩展性:随着 C++ 语言的发展,新的库和特性不断加入,命名空间的使用确保了新旧代码可以和谐共存,不会因新特性的加入而导致现有代码出现问题。
namespace定义
- 定义命名空间,需要使用到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
- namespace本质是定义出⼀个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand不在冲突了。
- C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量生命周期。
- namespace只能定义在全局,当然他还可以嵌套定义。
- 项目工程中多文件中定义的同名namespace会认为是⼀个namespace,不会冲突。
- C++标准库都放在⼀个叫std(standard)的命名空间中
- namespace是C++基础关键字,不需要什么头文件
namespace单层的书写和调用
这里Js是命名空间的名字,一般在开发里面用项目的名字为命名空间的名字
这里我是随意起了一个名字
域里面也是可以书写函数和结构体等等的
//namespace只能定义在全局,当然他还可以嵌套定义。 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> namespace Js { int c = 10; int ADD(int a, int b) { return a + b; } } int main() { printf("%d", Js::c); return 0; }
#include<stdio.h>
:这是另一个预处理指令,用于包含标准输入输出库的头文件。这个库提供了printf
函数,它在main
函数中被用来输出。
namespace Js { ... }
:这里定义了一个名为Js
的命名空间。命名空间是一种封装机制,用于组织代码,防止命名冲突。在这个命名空间中:
int c = 10;
定义了一个名为c
的整型变量,并初始化为10
。int ADD(int a, int b) { ... }
定义了一个名为ADD
的函数,它接受两个整型参数a
和b
,并返回它们的和。
int main() { ... }
:这是程序的主函数,程序从这里开始执行。
printf("%d", Js::c);
在这里,Js::c
表示对命名空间Js
中的成员变量c
的引用。printf
函数使用Js::c
作为参数,输出c
的值,即10
。总结来说,
Js::c
是对Js
命名空间中定义的静态成员变量c
的引用。在main
函数中,通过Js::c
访问该变量,并使用printf
函数将其值输出到控制台。这里可以看到结果,进入到命名空间里面访问c,c我们初始化给的数值是10,所以访问的时候就是10
::域作用限定符
域作用限定符这里主要的作用就是访问域里面的变量或者函数
下面的如果看不懂没关系,直到域作用限定符在这里面是干什么用的就可以
访问全局变量或函数:当局部作用域(如函数内部)中存在与全局作用域中同名的变量或函数时,可以使用
::
来指定访问全局作用域中的实体。//C++的输出方式 int globalVar = 10; // 全局变量 void function() { int localVar = 20; // 局部变量 std::cout << ::globalVar << std::endl; // 访问全局变量 }
//C语言的输出方式 int a = 110; int main() { printf("全局变量的a=110,局部变量的a=11\n\n\n"); int a = 11; printf("不加域作用限定符:%d\n\n\n", a); printf("加上域作用限定符:%d\n\n\n", ::a); }
访问命名空间中的成员:在包含多个命名空间的情况下,
::
用于指定访问特定命名空间中的成员。namespace A { int value = 5; } namespace B { int value = 10; } int main() { std::cout << ::A::value << std::endl; // 输出 A 命名空间中的 value std::cout << ::B::value << std::endl; // 输出 B 命名空间中的 value }
访问类的静态成员:在类的作用域内部访问静态成员时,需要使用
::
来指定。class MyClass { public: static int staticVar; }; int MyClass::staticVar = 0; // 定义静态成员 void function() { MyClass::staticVar = 1; // 在类外访问静态成员 }
解决作用域冲突:当嵌套作用域中存在同名实体时,
::
用于解决这些冲突,明确指定要访问的实体。int outerVar = 1; void outerFunction() { int innerVar = 2; std::cout << ::outerVar << std::endl; // 访问外部变量 }
访问枚举类型的值:在 C++11 之前,枚举类型没有强类型支持,使用
::
可以访问枚举中的值。enum Color { RED, GREEN, BLUE }; int main() { std::cout << ::RED << std::endl; // 访问 RED 枚举值 }
namespace嵌套的书写和调用
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> namespace Js { int c = 10; int ADD(int a, int b) { return a + b; } namespace js1 { int d = 30; int f = 20; int del(int d, int f) { return d - f; } } namespace js2 { int d = 30; int f = 20; int ADD(int d, int f) { return d + f; } } } int main() { printf("namespace单层调用测试:%d\n", Js::c); int ret = Js::js1::del(30, 10); printf("namespace多层调用测试:%d", ret); return 0; }
最终效果
- 在调用的时候,我们可以利用域作用限定符
- 进入到我们需要的域里面调用函数,并且选择接收。
- 我们既可以初始化,也可以选择传参,和C语言里面函数的使用是一样的。只是多了一层的封装。
namespace多文件定义-同变量名
在 C++ 中,命名空间(
namespace
)是一种封装机制,用于组织代码并防止名称冲突。当你在多个文件中定义同名的命名空间时,C++ 编译器会将这些命名空间视为同一个命名空间的扩展。这意味着不同文件中定义的同名命名空间的成员实际上是共享的,它们属于同一个逻辑上的命名空间。这种特性非常有用,因为它允许你将大型项目分解为多个模块或文件,每个模块都有自己的命名空间,同时又能够保持命名空间的一致性和统一性。
File1.cpp:
namespace MyProject { void function1() { std::cout << "Function 1" << std::endl; } }
File2.cpp:
namespace MyProject { void function2() { std::cout << "Function 2" << std::endl; } }
在这两个文件中,我们都定义了名为
MyProject
的命名空间,并在其中分别定义了function1
和function2
。当我们编译和链接这两个文件时,MyProject
命名空间将被视为同一个命名空间,function1
和function2
都将是MyProject
命名空间的成员。你可以在
main
函数或其他任何地方这样调用它们int main() { MyProject::function1(); // 调用 File1.cpp 中定义的 function1 MyProject::function2(); // 调用 File2.cpp 中定义的 function2 return 0; }
等同于一个域:
多⽂件中可以定义同名namespace,他们会默认合并到⼀起,就像同⼀个namespace⼀样
namespace MyProject { void function1() { std::cout << "Function 1" << std::endl; } void function2() { std::cout << "Function 2" << std::endl; } }
当然,如果你两个域封装的函数名字也一样,但是作用不一样,那么我们只需要再封装一下就可以。
namespace实际项目举例
实际使用
如图
当为同一个命名空间下面,我们还可以嵌套定义。
防止同一个项目组命名冲突
这样就增加了团队合作的协作性,而且这里有一个关键,是C语言没有的,就是不同域之间的命名冲突是不会导致报错的,因为域的不同,但是如果域相同命名一样,还是会导致冲突,解决方式就只需要再封装一下就可以,所以这里我们就可以发现C++比C语言优势多了很多了
namespace命名空间的展开
命名空间的展开顾名思义就是不需要继续进入到域里面了,直接就可以访问
命名空间展开的弊端和使用规则:
展开命名空间(风险很大,平时写的小算法,写的小项目,没有那麽多的冲突的时候,是可以直接展开,命名空间)
展开指定的命名空间:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> namespace Js { int c = 10; int ADD(int a, int b) { return a + b; } namespace js1 { int d = 30; int f = 20; int del(int d, int f) { return d - f; } } namespace js2 { int d = 30; int f = 20; int ADD(int d, int f) { return d + f; } } } int a = 110; //int main() //{ // printf("全局变量的a=110,局部变量的a=11\n\n\n"); // int a = 11; // printf("不加域作用限定符:%d\n\n\n", a); // printf("加上域作用限定符:%d\n\n\n", ::a); // // printf("namespace单层调用测试:%d\n\n\n", Js::c); // // int ret = Js::js1::del(30, 10); // printf("namespace多层调用测试:%d\n\n", ret); // // //展开命名空间 // // return 0; //} using namespace Js; int main() { int c = 0; printf("展开命名空间,访问局部空间有的变量:%d\n\n\n", c); printf("展开命名空间,访问局部空间有的变量:%d", ::c); return 0; }
- 这里可以发现,::域作用限定符存在依旧是会首先访问全局变量,不存在会首先访问局部变量
- 这里已经展开Js这个域,所以c就暴露在全局变量了
展开嵌套的命名空间:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> namespace Js { int c = 10; int ADD(int a, int b) { return a + b; } namespace js1 { int d = 30; int f = 20; int del(int d, int f) { return d - f; } } namespace js2 { int d = 30; int f = 20; int ADD(int d, int f) { return d + f; } } } int a = 110; //int main() //{ // printf("全局变量的a=110,局部变量的a=11\n\n\n"); // int a = 11; // printf("不加域作用限定符:%d\n\n\n", a); // printf("加上域作用限定符:%d\n\n\n", ::a); // // printf("namespace单层调用测试:%d\n\n\n", Js::c); // // int ret = Js::js1::del(30, 10); // printf("namespace多层调用测试:%d\n\n", ret); // // //展开命名空间 // // return 0; //} using namespace Js; using namespace Js::js2; int main() { int c = 0; printf("展开外层命名空间,访问局部空间有的变量:%d\n\n\n", c); printf("展开外层命名空间,访问局部空间有的变量:%d\n\n\n", ::c); int d = 00; printf("展开嵌套内层命名空间,访问局部空间有的变量:%d\n\n\n", d); printf("展开嵌套内层命名空间,访问局部空间有的变量:%d\n\n\n", ::d); return 0; }