C++之名字空间(namespace)

C++之名字空间(namespace) 一、名字空间 namespace1.1 为什么需要名字空间核心作用解决命名冲突解决 C 语言全局变量、全局函数命名冲突问题将代码分组隔离不同空间内的同名标识符互不干扰相当于给代码添加专属“作用域前缀”注意不改变变量的生命周期、存储位置只是从名字做了隔离生活化类比同一个文件夹里不能有两个同名的文件但放在不同文件夹里同名文件就可以共存用文件夹路径区分即可。对应到代码如果两个程序员都写了同名的函数、变量、类放在一起编译就会报重定义错误。1.2 什么是名字空间1.2.1 名字空间定义namespace 空间名字 { // 里面可以放变量、函数、类、结构体、另一个名字空间名字空间嵌套 int num 0; void func() {/* ... */} class Student {/*...*/} } /* 名字空间的大括号后面不用加分号和类不一样 同一个名字空间可以分开写多次甚至跨文件写编译器会自动把它们合并成一个 名字空间只能定义在全局范围或者另一个名字空间内部不能写在函数里面 */1.2.2 访问语法空间名::成员名。:: 为作用域解析符释义为“的”。1.3 怎样用名字空间——三种访问方式1.3.1 全前缀访问最规范、最安全、推荐正式项目用// 直接写 空间名::成员名明确指定来自哪个名字空间 A::num 20; A::func();1.3.2 单个成员引入// 用 using 空间名::成员名; 声明之后这个成员就可以直接用名字访问了。 using A::num; // 只把 A 里的 num 放出来 num 30; // 等价于 A::num 30;1.3.3 整个名字空间展开练习/小项目使用// 用 using namespace 空间名;把这个名字空间里的所有东西全部放到全局作用域之后可以直接写名字调用。 using namespace A; num 40; // 直接用 func(); // 直接用1.3.4 最常用的名字空间stdC 标准库提供的所有功能——cout、cin、string、endl、vector等等全部都放在std这个名字空间里。这么设计的原因就是避免冲突标准库有几百个名字如果都放在全局很容易和你自己写的变量、函数重名。放进std里默认不会和你的代码冲突需要用的时候再展开或者加前缀。1.3.5 三种方式对比1.3.6 延伸知识同名空间自动合并多次定义同名空间内容自动追加不会覆盖名字空间起别名如果名字空间的名字太长可以给他起个短别名namespace CG China::Guangdong; cout CG::city endl; // 和 China::Guangdong::city 效果一样空间嵌套支持多层嵌套。通过多级 :: 访问类似文件夹分层。namespace China { namespace Guangdong { string city 深圳; } } // 访问方式逐层加 :: cout China::Guangdong::city endl;匿名空间无名称的空间内部内容仅当前文件可用替代 C 语言 static 全局限定。namespace { int file_only 100; }二、名字空间声明 VS 名字空间指令2.1 名字空间声明语法using 命名空间名::成员名核心作用精准引入命名空间中单个指定成员将该名字直接写入当前作用域的定义表属于高优先级名字。2.2 名字空间指令语法using namespace 命名空间名核心作用批量引入命名空间所有成员变量仅将名字加入当前作用域的可见表不会写入当前作用域的定义表属于低优先级名字// 名字空间声明 和 名字空间指令 的差别 #include iostream using namespace std; namespace ns1 { int g_value 0; int g_other 0; } namespace ns2 { int g_value 0; int g_other 0; } int main(void) { // 名字空间指令将 ns1 所有成员引入当前作用域低优先级属于“可见”级别 using namespace ns1; // 名字空间声明仅将 ns2::g_value 引入当前作用域高优先级属于“定义”级别 using ns2::g_value; g_value 666; // 优先匹配 名字空间声明 的 ns2::g_value cout ns1::g_value ns1::g_value , ns2::g_value ns2::g_value endl; g_other 999; // 仅 ns1 通过指令批量引入因此匹配 ns1::g_other cout ns1::g_other ns1::g_other , ns2::g_other ns2::g_other endl; return 0; }三、代码实战#include iostream using namespace std; // 基础命名空间 namespace ns1 { int g_value 10; int g_other 20; } namespace ns2 { int g_value 30; int g_other 40; } // 嵌套命名空间演示 namespace outer_ns { int num 100; namespace inner_ns { int num 200; } } int main(void) { // 1. 优先级验证单个声明 批量指令 cout 1. 优先级验证 endl; { using namespace ns1; using ns2::g_value; g_value 666; // 优先匹配 ns2::g_value g_other 999; // 匹配批量引入的 ns1::g_other cout ns1::g_value ns1::g_value endl; cout ns2::g_value ns2::g_value endl; cout ns1::g_other ns1::g_other endl; cout ns2::g_other ns2::g_other endl; } // 2. 批量引入两个命名空间同名变量产生二义性取消注释会编译报错 cout \n 2. 二义性冲突演示 endl; /* { using namespace ns1; using namespace ns2; g_value 100; // 编译错误g_value 指代不明确 // 必须显式指定命名空间才能正常使用 ns1::g_value 100; ns2::g_value 200; } */ // 3. using 的局部作用域 cout \n 3. using 局部作用域 endl; { using ns1::g_value; g_value 111; // 仅在当前代码块内有效 cout 块内 ns1::g_value ns1::g_value endl; } ns1::g_value 222; // 出块后 using 失效必须显式指定命名空间 cout 块外 ns1::g_value ns1::g_value endl; // 4. 命名空间别名简化长命名空间书写 cout \n 4. 命名空间别名 endl; namespace inner outer_ns::inner_ns; cout 嵌套命名空间值 inner::num endl; return 0; }3.1 核心疑问解答g_value 为何不冲突报错代码中 ns1 和 ns2 均存在 g_value但 g_value 666; 不会报歧义错误核心原因是两者名字查找优先级不同不在同一竞争层级高优先级using 声明using ns2::g_value 将名字写入 main 当前作用域定义表编译器优先查找该层级低优先级using 指令using namespace ns1 仅将名字加入可见表不参与当前作用域优先级竞争C 查找铁律名字从内到外、从高优先级到低优先级查找找到第一个匹配项立即终止查找。因此编译器优先匹配到 ns2::g_value直接忽略 ns1::g_value无歧义、不报错。3.2 g_other 赋值逻辑说明代码中无任何针对 g_other 的 using 声明当前作用域定义表无匹配名字编译器降级查找可见表最终匹配到 ns1::g_other因此赋值 999 生效于 ns1 的成员。3.3 高危报错3.4 C 名字查找优先级局部变量 当前作用域 using 声明 外层作用域变量 using 指令引入的成员