【C++】命名空间namespace

1,456 阅读3分钟

最近在工作中遇到一个bug,找问题找了很久,最终发现是函数命名冲突的原因。具体地说就是上层在调用接口的时候使用的是我的头文件,但是实际调用到的实现来自其他静态库中的同样没有命名空间的同名函数。在工程联调的时候当断点打在该函数时无法跳转进我的函数实现,而是直接跳转到汇编语言,证明是链接到了已经编译好的其他静态库中。在这种情况下将自己的函数定义或类定义全部放在一个自己定义的namespace中,便可以避免这种问题出现。

命名空间简介

首先来看cppreference对namespace的几句定义:

  • namespace向使用者提供一种避免函数名称冲突的方式。
  • 定义在一个namespace块内的函数符号,会被放入一个单独的命名范围,可以防止与其他命名范围中相同名称的函数符号混淆。
  • 同一个namespace可以定义多个块,他们是属于同一个命名范围的。
  • namespace在编译过程中是一个translation unit最外层的声明。
# 命名空间定义
# 一般命名空间定义
namespace ns_name { ... } 
# 内联命名空间定义(C++11)
# 内联命名空间可以使命名空间包含的内容直接导出,这样该命名空间外部使用时不必额外加上命名空间的前缀
# 必须在第一次定义(original-namespace-definition)的时候显式加入前缀inlier
# 后面再次使用时(extension-namespace-definition)便可以隐式声明
inline namespace ns_name { ... }
# 无名命名空间定义:内部成员在其声明点生效,直至tranlation unit结束
namespace { ... }
# 命名空间嵌套定义,下面两种写法等价
namespace A::B::C { ... }
namespace A { namespace B { namespace C { ... } } }

# 命名空间使用
# 调用命名空间内的类或方法
ns_name::method_name
ns_name::class_name
# 声明使用命名空间
using namspace ns_name;
# 声明使用命名空间内的某类/方法,在下文中可以直接使用method_name来调用
using ns_name::method_name;

内联命名空间(inline namespace)

关于内联命名空间,他的实现方式可以理解为在其namepace外部隐式的插入了using namespace ns_name;,这样相当于将命名空间展开,可以直接调用到内部代码。
这里多提一句,关于内联命名空间的用处,看到一个博客写的比较清楚,如果你是开发人员,当对软件做版本迭代的时候需要满足以下几个条件:

  • 版本更新时使用者无需修改自己的代码,便可以使用最新的版本
  • 使用者可以使用到新版本添加的新功能
  • 如果有需要使用者也可以通过修改代码调用旧的版本
# 旧版本代码
class A(){
public:
    void func(){}
}
class B(){
public:
    void func(){}
}
# 那么用户在调用时:
void user_invoker(){
    A a;
    a.func();
    B b;
    b.func();
}

那么在版本迭代的时候我们只需按如下方式修改代码:

namespace ver1{
    class A(){
    public:
        void func(){}
    }
    class B(){
    public:
        void func(){}
    }
}
inline namespace ver2{
    class A(){
    pulic:
        void func(){}
    }
    class B(){
    public:
        void func(){}
    }
    class C(){
    pulic:
        void func(){}
    }
}

如此一来,使用者无需更改自己的代码便可以保证调到class Aclass B的最新版本,并且可以通过添加代码来使用新加入的class C,如果使用者想要用到旧的版本,只需在对应类/函数前面加上旧版本的命名空间即可。

ver1::A a_ver1();
a_ver1.func();

无名命名空间(Unnamed namespace)

无名命名空间可以理解为有一个独一无二名称的命名空间,而且仅在他被声明的范围(scope)内使用其内部代码,同样可以理解为在声明处隐式使用了using namespace unique

namespace A {
    namespace {
        int i;
    }
    void func(){
        i++;
    }
}
using namespace A;
void func2(){
    i++;
}

最后顺便提一点,命名空间也可以使用类似“别名”的方式来定义和使用:

namespace A {
    namespace B {
        namespace C {
            int i = 10;
        }
    } 
}
namespace ABC = A::B::C;
int main(){
    ABC::i += 10;
}