[c++] namespace
一、热身问题
Ref: C++/C++11中命名空间(namespace)的使用
多个库将名字放置在全局命名空间中将引发命名空间污染(namespace pollution)。
- 命名空间既可以定义在全局作用域内,也可以定义在其它命名空间中,
- 但是不能定义在函数或类的内部。
- 命名空间作用域后面无须分号。
链接:https://www.zhihu.com/question/382434515/answer/1104683131
命名空间的定义可以不连续的特性使得我们可以将几个独立的接口和实现文件组成一个命名空间。
成员函数变 static 函数,基本上是没有设计意义的。程序的执行效率变化也要看具体情况。更重要的是,规范告诉我们,提倡的是封在 namespace 里的自由函数,而不是封在 class/struct 里的 static 函数。名字空间就是为这个目的而发明的,它可以嵌套,可以发挥重载函数和操作符的 ADL 名字搜索(优良的 ADL 是另一个设计问题)。
类里面的 static 函数有真正设计意图的用法:
- 单例类的对象取回函数,通常又和对象工厂模式混合在一起。
- 特征模版类 traits 及其特化中的 static 函数,为了转发类型自适应参数的调用。参考
std::char_traits
[1] 中的copy()
、compare()
等 static 函数。
二、命名空间类型
全局命名空间 (global namespace)
int a = 10; // <---- global namespace namespace N { int a = 100; void f() { int a = 1000; std::cout << a << std::endl; //prints 1000 std::cout << N::a << std::endl; //prints 100 std::cout << ::a << std::endl; //prints 10 } }
内联命名空间 (inline namespace)
它的特点就是不需要使用using语句就可以直接在外层命名空间使用该命名空间内部的内容,而且无需使用命名空间前缀。
内联命名空间的应用场景相当有限,但是一旦用上了,还是比较神奇的。

#include<iostream> using namespace std; namespace ver1 { class A { public: void func1() {} }; } namespace ver1 { class B { public: void func1() {} }; } // void inline_demo() // { // A a; // a.func1(); // // B b; // b.func1(); // } // 以下是新更新的函数,也就是升级的函数 inline namespace ver2 { class A { public: void func2() {} }; } // 隐式inline namespace ver2 { class B { public: void func1 () {} }; class C { public: void func3 () {} }; } void inline_demo2() { A a; a.func2(); B b; b.func1(); C c; c.func3(); ver1::A a1; a1.func1(); }
未命名的命名空间 (unnamed namespace)
未命名的命名空间仅在特定的文件内有效,其作用范围 不会横跨多个不同的文件。
顶替 global static
未命名的命名空间取代文件中的静态声明:在标准C++引入命名空间的概念之前,程序需要将名字声明成static的以使得其对于整个文件有效。在文件中进行静态声明的做法是从C语言继承而来的。在C语言中,声明为static的全局实体在其所在的文件外不可见。在文件中进行静态声明的做法已经被C++标准取消了,现在的做法是使用未命名的命名空间。当你遇到这样的情况,需要将全局符号声明成 static 以避免链接问题时,优先使用未命名命名空间:
1. 在你的源文件中声明一个没有名字的命名空间。
2. 将全局函数或变量的定义放在未命名命名空间中,无须将它们声明为 static。
c语言的 static 修饰函数
当在一个编译单元中声明一个函数时,它具有外部链接性。这就是说两个不同编译单元中的两个具有相同名字的函数将生成一个链接错误,因为不能存在具有相同名字的两个符号。这个问题在 C 中已经解决,在 C++ 中也解决了部分,就是将函数或变量声明成 static 从而将它的链接性从外部改为内部。在这种情况下,它的名字不再导出到外部的编译单元中,从而避免了这个链接问题。
// file1.cpp namespace { void print(std::string message) { std::cout << "[file1] " << message << std::endl; } } void file1_run() { print("run"); // 这个肯定是本文件的print,而不是file2定义的print }
// file2.cpp namespace { >> void print(std::string message) { std::cout << "[file2] " << message << std::endl; } } void file2_run() { print("run"); }
命名空间的别名 (namespace alias)
一个命名空间可以有好几个同义词或别名,所有别名都与命名空间原来的名字等价。
namespace foo { namespace bar { namespace baz { int qux = 42; } } } namespace fbz = foo::bar::baz; int main() { std::cout << fbz::qux << '\n';
}
using声明
一条using声明 (using declaration) 语句一次只引入命名空间的一个成员。它使得我们可以清楚地知道程序中所用的到底是哪个名字。
相比于使用using指示,在程序中对命名空间的每个成员分别使用using声明效果更好,这么做可以减少注入到命名空间中的名字数量。
三、代码操练
#include "namespace.hpp" #include <iostream> #include <vector> #include <string> namespace namespace_ { //////////////////////////////////////////////////// // reference: http://en.cppreference.com/w/cpp/language/namespace namespace A { int x; } namespace B { int i; struct g { }; struct x { }; void f(int) { fprintf(stdout, "%s, %d\n", __FUNCTION__, __LINE__); }; void f(double) { fprintf(stdout, "%s, %d\n", __FUNCTION__, __LINE__); }; void g(char) { fprintf(stdout, "%s, %d\n", __FUNCTION__, __LINE__); }; // OK: function name g hides struct g } int test_namespace_1() { int i; //using B::i; // error: i declared twice void f(char); using B::f; // OK: f(char), f(int), f(double) are overloads f(3.5); // calls B::f(double) using B::g; g('a'); // calls B::g(char) struct g g1; // declares g1 to have type struct B::g using B::x; using A::x; // OK: hides struct B::x x = 99; // assigns to A::x struct x x1; // declares x1 to have type struct B::x return 0; } ///////////////////////////////////////////////////////// // reference: http://en.cppreference.com/w/cpp/language/namespace namespace D { int d1; void f(char) { fprintf(stdout, "%s, %d\n", __FUNCTION__, __LINE__); }; } using namespace D; // introduces D::d1, D::f, D::d2, D::f, // E::e, and E::f into global namespace! int d1; // OK: no conflict with D::d1 when declaring namespace E { int e; void f(int) { fprintf(stdout, "%s, %d\n", __FUNCTION__, __LINE__); }; } namespace D { // namespace extension int d2; using namespace E; // transitive using-directive void f(int) { fprintf(stdout, "%s, %d\n", __FUNCTION__, __LINE__); }; } int test_namespace_2() { //d1++; // error: ambiguous ::d1 or D::d1? ::namespace_::d1++; // OK D::d1++; // OK d2++; // OK, d2 is D::d2 e++; // OK: e is E::e due to transitive using //f(1); // error: ambiguous: D::f(int) or E::f(int)? f('a'); // OK: the only f(char) is D::f(char) return 0; } ////////////////////////////////////////////////////////// // reference: http://en.cppreference.com/w/cpp/language/namespace namespace vec { template< typename T > class vector { // ... }; } // of vec int test_namespace_3() { std::vector<int> v1; // Standard vector. vec::vector<int> v2; // User defined vector. //v1 = v2; // Error: v1 and v2 are different object's type. { using namespace std; vector<int> v3; // Same as std::vector v1 = v3; // OK } { using vec::vector; vector<int> v4; // Same as vec::vector v2 = v4; // OK } return 0; } /////////////////////////////////////////////////////// // reference: https://msdn.microsoft.com/en-us/library/5cb46ksf.aspx /*namespace Test { namespace old_ns { std::string Func() { return std::string("Hello from old"); } } inline namespace new_ns { std::string Func() { return std::string("Hello from new"); } } } int test_namespace_4() { using namespace Test; using namespace std; string s = Func(); std::cout << s << std::endl; // "Hello from new" return 0; } */ /////////////////////////////////////////////////////// // reference: https://www.tutorialspoint.com/cplusplus/cpp_namespaces.htm // first name space namespace first_space { void func() { std::cout << "Inside first_space" << std::endl; } // second name space namespace second_space { void func() { std::cout << "Inside second_space" << std::endl; } } } int test_namespace_5() { using namespace first_space::second_space; // This calls function from second name space. func(); return 0; } ///////////////////////////////////////////////////// // reference: http://www.geeksforgeeks.org/namespace-in-c/ // A C++ code to demonstrate that we can define methods outside namespace. // Creating a namespace namespace ns { void display(); class geek { public: void display(); }; } // Defining methods of namespace void ns::geek::display() { std::cout << "ns::geek::display()\n"; } void ns::display() { std::cout << "ns::display()\n"; } int test_namespace_6() { ns::geek obj; ns::display(); obj.display(); return 0; } } // using namespace_
End.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律