ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

C++命名空间:从“撞名”烦恼到using避坑,一篇彻底搞懂!

C++命名空间:从“撞名”烦恼到using避坑,一篇彻底搞懂! 1.应用领域服务器端游戏引擎机器学习引擎音视频处理嵌入式软件电信设备金融应用基础库操作系统编译器基础架构基础工具硬件互动等2.命名空间命名空间是 C 用于隔离标识符名称的语法机制本质是划定一块独立作用域将变量、函数、类等标识符收纳在这个作用域内用来解决多文件协作、引入第三方库时产生的命名冲突问题。命名空间仅在编译期生效本身不占用内存还可以为较长的命名空间定义别名简化代码书写。命名空间内部的成员不会自动暴露到外部访问成员时需要通过命名空间名称进行限定。在C语言中可能会与库函数名出现冲突C的命名空间namespace则可以顺利解决该问题2.1定义命名空间要用到namespace关键字后面跟有命名空间的名字然后接一对{}{}中即为命名空间的成员命名空间中可以定义变量函数甚至结构体等等namespace N { typedef int STDataType; // 动态顺序栈结构体 typedef struct Stack { STDataType* a; // 动态数组存放栈元素 int top; // 栈顶标记代表下一个要入栈的位置 int capacity; // 数组总容量 }ST; // 栈接口声明 void STInit(ST* ps, int n); void STDestroy(ST* ps); void STPush(ST* ps, STDataType x); void STPop(ST* ps); STDataType STTop(ST* ps); int STSize(ST* ps); bool STEmpty(ST* ps); }// Stack.cpp函数的定义 #includeStack.h namespace bit { // 栈初始化预开辟n个元素的空间 void STInit(ST* ps, int n) { assert(ps); ps-a (STDataType*)malloc(n * sizeof(STDataType)); ps-top 0; ps-capacity n; } // 入栈 void STPush(ST* ps, STDataType x) { assert(ps); // 判断栈是否已满满则扩容 if (ps-top ps-capacity) { printf(扩容\n); // 空栈初始给4否则容量翻倍 int newcapacity ps-capacity 0 ? 4 : ps-capacity * 2; STDataType* tmp (STDataType*)realloc(ps-a, newcapacity * sizeof(STDataType)); if (tmp NULL) { perror(realloc fail); return; } ps-a tmp; ps-capacity newcapacity; } ps-a[ps-top] x; ps-top; } // 其余栈接口STPop、STTop、STSize、STEmpty、STDestroy 省略 }//全局变量的使用和命名空间namespace的使用区别 // 全局版本栈的函数 void STInit(ST* ps) {} void STPush(ST* ps, int x) {} int main() { // 使用【全局】的ST结构体与函数 ST st1; STInit(st1); STPush(st1, 1); STPush(st1, 2); printf(%d\n, sizeof(st1)); // 使用【bit命名空间】内的ST结构体与函数用::访问 bit::ST st2; printf(%d\n, sizeof(st2)); bit::STInit(st2); bit::STPush(st2, 1); bit::STPush(st2, 2); return 0; }2.2命名空间namespace本质其本质是一个域该域与全局域相互独立不同的域可以定义同名变量不会产生同命名冲突int x 0; namespace A { int x 1; } namespace B { int x 2; }::x // 全局的 x在::域作用限定符前无指定域故指向全局域A::x // A 里的 xB::x // B 里的 x2.3C的域C的域有函数局部域全局域命名空间域类域域影响的是编译查找逻辑还会影响变量的生命周期命名空间域和类域不影响变量生命周期。C 标准库的所有接口就统一放置在 std 命名空间中。2.4注意命名空间只能定义在全局命名空间内可以进行嵌套定义要从外向内进行指定域namespace A { namespace B { int x 0; } namespace C { int x 10; } } int main() { printf(%d\n, A::B::x); printf(%d\n, A::C::x); }注意嵌套层级关系这里A::N和::N是两个不同的命名空间不会合并。namespace A { namespace N { int x; } } namespace N { int y; }2.5多文件中定义同名namespace编译器会自动合并视为同一个命名空间// a.cpp namespace N { void f() {} } // b.cpp namespace N { void g() {} }//等效于 // main.cpp namespace N { void f(); void g(); } int main() { N::f(); N::g(); }//同一个文件下也可适用 namespace N { int a; } namespace N { int b; } //等效于 namespace N { int a; int b; }注意但要注意合并的是命名空间本身不是里面的实体。也就是说namespace N { int x; } namespace N { int x; // 错误同一个 N 中重复定义 x }但是可以这样因为这是同一个函数的声明和定义。namespace N { void f(); // 声明 } namespace N { void f() {} // 定义 }2.6其他使用命名空间仍存在需要留意的地方如不要在头文件中使用 using namespace 指令头文件 会被多个源文件包含会大范围引入命名空间标识符极易引发命名冲突使用 using 声明或者 using 指令时其作用范围仅限于当前作用域不会向外扩散命名空间只隔离名字命名空间内部定义的全局变量依旧拥有全局生命周期工程开发中不建议滥用 using namespace std优先使用 std:: 限定的方式访问标准库成员降低命名冲突的风险。2.7域的查找规则情景一无指定域先在局部域中查找当前所在域中再在全局域中查找namespace A { int rand 10; int ADD(int a, int b) { return a b; } struct Node { struct Node* next; int data; }; } int main() { printf(%d\n, rand);//rand是在库函数中未指定域在这里直接打印出的是rand函数地址的十进制 printf(%d\n, A::rand);//指定是A所在域的rand10 }情景二有指定域现在指定域中查找再去所在的局部域中查找最后在全局域中查找3.命名空间namespace使用编译查找一个声明或者定义时默认只会在全局或者局部中查找不会在命名空间域中查找所以要使用命名空间中定义的变量或者函数等等有三种形式Ⅰ指定命名空间访问大量访问时不推荐使用namespace A { int x 1; } namespace B { int x 2; } int main() { int a A::x; // 访问 A 中的 x int b B::x; // 访问 B 中的 x }Ⅱusing将命名空间中某个成员展开项目中访问某个常用且不存在冲突的成员时推荐如using A::STPush()是一种部分展开这种写法会在局部域中查找全局域中查找命名空间域中查找使用命名空间内的函数或者变量无需再去写A::XXusing std::cout; // using 声明只引入 cout using namespace std; // using 指令引入整个 std 命名空间注意会和局部域或者其他域中同名的变量出现冲突namespace N { int x; } int main() { using N::x; int x 1; // 错误重复定义 }Ⅲ展开命名空间的全部成员项目中不推荐冲突风险大写法using namespace 命名空间名;缺点会把指定命名空间中的全部成员名字引入当前作用域让它们在未限定名字查找中可见。namespace N { int x; void f(); } using namespace N; int main() { x 1; // 可以相当于找到 N::x f(); // 可以相当于找到 N::f }理解using namespace A;不是把 A 的成员复制到当前命名空间也不是让它们变成当前命名空间的成员。它只是影响名字查找从using namespace A;出现的位置开始到它所在作用域结束这期间写未限定名时编译器会把N中的名字也纳入查找范围。注意//错误写法 namespace N { int x; } namespace X { using namespace N; } X::x; // 一般不行x 并不是 X 的成员 //正确写法 x; // 在 X 作用域内可能通过未限定查找找到 N::x N::x; // 显式限定总是可以//产生命名歧义 namespace A { int x; } namespace B { int x; } using namespace A; using namespace B; int main() { x 1; // 错误A::x 还是 B::x } //纠正 A::x 1; B::x 2;综上所述项目中更推荐using N::member;或直接写N::member避免在头文件和全局作用域使用using namespace。
返回列表