c++基础篇(三)——命名空间
前言:
与c相比,命名空间是c++的一大特色,这样的设计注定了c++是面向超大型编程项目的。
1 介绍
为什么需要命名空间?在c中并不支持命名命名空间,c在大型项目中会遇到烦人的命名重复的问题。大型工程中,不同的模块会分配给不同的人或团队开发,各自模块测试通过,最终合到一起时可能会发现命名重复,给模块设计带来额外的负担。使用第三方代码时,也存在与其命名重复的可能,c++引入命名空间可以很好的解决大型项目中命名重复的问题。
2 定义命名空间
c++中通过关键词 namespace 定义一个命名空间,在这个命名空间内定义的变量或函数即属于这个命名空间,必须通过该命名空间才能访问到。以下代码定义了命名空间 MyNameSpace ,并且通过命名空间名访问了命名空间内的函数和变量,访问通过 :: 符号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <iostream>
namespace MyNameSpace { int Val1 = 1; int Val2 = 2;
int Add(int Num1, int Num2) { return Num1 + Num2; } }
int main(void) { int Rtn = 0U;
Rtn = MyNameSpace::Add(MyNameSpace::Val1, MyNameSpace::Val2); std::cout << Rtn << std::endl;
return 0; }
|
一个源文件内可同时定义多个命名空间,不同命名空间内的命名可以重复,通过命名空间名来区分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <iostream>
namespace MyNameSpaceFirst { int Val1 = 1; int Val2 = 2;
int Add(int Num1, int Num2) { return Num1 + Num2; } }
namespace MyNameSpaceSecond { int Val1 = 11; int Val2 = 22; int Val3 = 33;
int Add(int Num1, int Num2, int Num3) { return Num1 + Num2 + Num3; } }
int main(void) { int Rtn = 0U;
Rtn = MyNameSpaceFirst::Add(MyNameSpaceFirst::Val1, MyNameSpaceFirst::Val2); std::cout << Rtn << std::endl;
Rtn = MyNameSpaceSecond::Add(MyNameSpaceSecond::Val1, MyNameSpaceSecond::Val2, MyNameSpaceSecond::Val3); std::cout << Rtn << std::endl;
return 0; }
|
命名空间可以多次定义,即继续往里添加内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include <iostream>
namespace MyNameSpace { int Val1 = 1;
int Add(int Num1, int Num2) { return Num1 + Num2; } }
namespace MyNameSpace { int Val2 = 2; }
namespace MyNameSpace { int Sub(int Num1, int Num2) { return Num1 - Num2; } }
int main(void) { std::cout << MyNameSpace::Val1 << std::endl; std::cout << MyNameSpace::Val2 << std::endl; std::cout << MyNameSpace::Sub(5, 1) << std::endl;
return 0; }
|
3 使用命名空间
命名空间定义后,通过关键词 using 指令导入一个名称空间进行使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <iostream>
namespace MyNameSpace { int Val1 = 1; int Val2 = 2; }
using namespace MyNameSpace;
int main(void) { std::cout << Val1 << std::endl; std::cout << Val2 << std::endl;
return 0; }
|
using namespace MyNameSpace
使用 using
导入 MyNameSpace
命名空间中的所有名称,就不需要每次在调用命名空间中成员时都加 <MyNameSpace>::
了,注意其有作用域。
命名空间也有作用域的限制,仅在其域内可见,以下是示例代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <iostream>
namespace MyNameSpace { int Val1 = 1; int Val2 = 2; }
int main(void) { { using namespace MyNameSpace;
std::cout << Val1 << std::endl; std::cout << Val2 << std::endl; }
return 0; }
|
若命名空间里的名称与局部变量同名,则会使用局部变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include <iostream>
namespace MyNameSpace { int Val1 = 1; int Val2 = 2; }
int main(void) { int Val1 = 1; int Val2 = 2;
{ using namespace MyNameSpace;
std::cout << Val1 << std::endl; std::cout << Val2 << std::endl; }
std::cout << Val1 << std::endl; std::cout << Val2 << std::endl;
return 0; }
|
输出结果为。
当命名空间中的名称与非局部名称同名时,编译器会报错,因为产生了二义性,程序不知道应该调用哪个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <iostream>
namespace MyNameSpace { int Val1 = 1; int Val2 = 2;
void Fun(void) { printf("Call Fun 1\n"); } }
int Val1 = 3; int Val2 = 4;
void Fun(void) { printf("Call Fun 2\n"); }
int main(void) { { using namespace MyNameSpace;
std::cout << Val1 << std::endl; std::cout << Val2 << std::endl; Fun(); }
std::cout << Val1 << std::endl; std::cout << Val2 << std::endl; Fun();
return 0; }
|
4 不同命名空间内成员同名
如果两个命名,比如变量名同名,但属于不同命名空间,可以通过命名空间名分别调用他们,语法为 <NameSpace>::<ObjectName>
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <iostream>
namespace MyNameSpace1 { int Val1 = 1; int Val2 = 2; }
namespace MyNameSpace2 { int Val1 = 3; int Val2 = 4; }
int main(void) { std::cout << MyNameSpace1::Val1 << std::endl; std::cout << MyNameSpace2::Val1 << std::endl;
return 0; }
|
输出结果为。
建议不止是在命名重复时用这种方式调用命名空间内成员,养成这种好的编码习惯可以预防将来发生命名重复。
5 命名空间新增成员
已经定义的命名空间如何继续往里新增成员,新增和定义的方式相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include <iostream>
namespace MyNameSpace { int Val1 = 1; int Val2 = 2;
void Fun(void) { printf("Call Fun 1\n"); } }
namespace MyNameSpace { int Val3 = 3; }
void Fun(void) { printf("Call Fun 1\n"); }
int main(void) { std::cout << MyNameSpace::Val1 << std::endl; std::cout << MyNameSpace::Val2 << std::endl; std::cout << MyNameSpace::Val3 << std::endl; Fun();
return 0; }
|
输出结果为。
6 命名空间嵌套
命名空间也支持嵌套,以下是代码示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <iostream>
namespace MyNameSpace1 { int Val1 = 1; int Val2 = 2;
namespace MyNameSpace2 { int Val1 = 3; int Val3 = 4; } }
int main(void) { std::cout << MyNameSpace1::Val1 << std::endl; std::cout << MyNameSpace1::MyNameSpace2::Val1 << std::endl;
return 0; }
|
输出结果为。
命名空间嵌套的不同层级之间成员名也可同名,通过加 <NameSpace>::
前缀来调用,前缀数量和顺序和嵌套层级对应。
也可以把头文件包含放到命名空间中,这样头文件中的成员都需要通过该命名空间进行访问,在一些大型项目中为了防止与第三方库的命名同名,会使用此方法,比如Qt6。
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <iostream> QT_BEGIN_NAMESPACE #include <QString> QT_END_NAMESPACE
int main() { using namespace Qt;
QString message = "Hello, World!"; std::cout << qPrintable(message) << std::endl;
return 0; }
|
有时嵌套层级太多了,为了省去前缀可以这样写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <iostream>
namespace MyNameSpace1 { int Val1 = 1; int Val2 = 2;
namespace MyNameSpace2 { int Val1 = 2; int Val3 = 3; } }
int main(void) { using namespace MyNameSpace1::MyNameSpace2;
std::cout << Val1 << std::endl;
return 0; }
|
输出结果为。