C++命名空间namespace

本文保存在gitee仓库,接受PR。

命名空间的作用是解决程序中常见的同名冲突。例如,我可能在编写一个库,里面具有Boss类,同时另外一个库里面也有一个Boss类,这时如果我写的程序需要这两个库,那么编译器就无法区分程序里使用的Boss类到底是哪一个库的。

C++的命名空间解决问题的办法是:

  1. 引入命名空间的概念,它的本质是一块作用域,用来划分同名函数、类、变量等;
  2. 不同命名空间内的函数、类、变量等标识符可同名;
  3. 命名空间可以嵌套;
  4. 最外面的全局作用域作为默认命名空间。

下面这个示例演示了如何利用命名空间来解决名字冲突的问题。假设当前项目文件夹有5个文件,my.hmy.cppanother.hanother.cppmain.cpp。其中myanother开头的文件里分别存在Boss类的声明和成员函数的实现,main.cpp使用它们提供的Boss类。文件内容分别如下:

  1. my.h
#ifndef MY_H
#define MY_H

namespace my {
    class Boss {
    public:
        void say();
    };
}

#endif
  1. my.cpp
#include "my.h"
#include 

namespace my {
    void Boss::say() {
        std::cout << "我是你大爷!" << std::endl;
    }
}
  1. another.h
#ifndef ANOTHER_H
#define ANOTHER_H

namespace another {
    class Boss {
    public:
        void say();
    };
}

#endif
  1. another.cpp
#include "another.h"
#include 

namespace another {
    void Boss::say() {
        std::cout << "我不是你大爷!" << std::endl;
    }
}
  1. main.cpp
#include "my.h"
#include "another.h"

int main() {
    my::Boss myBoss;
    another::Boss anotherBoss;
    myBoss.say();
    anotherBoss.say();
    return 0;
}

可以看到,main.cpp中通过my::Bossanother::Boss使用了命名空间myanother下的Boss类,这样就算类名称相同也不会有问题。编译以上的代码并执行可以得到预期的结果:

$ g++ my.cpp another.cpp main.cpp
$ ./a.out 
我是你大爷!
我不是你大爷!

关于命名空间的其它细节

命名空间名称的命名规则

命名空间的名字位于关键字namespace之后,它必须是合法标识符,也就是由大写字母、小写字母、下划线和数字0~9,且以非数字字符开头的字符组成的字符序列。例如:

namespace abc123_123_ABC {} // 正确
namespace _______0_abc {}   // 正确
namespace MyNamespace {}    // 正确
namespace 0_abc {}          // 错误
namespace +abc {}           // 错误
namespace ?lKk {}           // 错误

命名空间名字是区分大小写的,例如MymyAnotheranother都是不同的命名空间。

命名空间成员

命名空间内声明的东西叫做命名空间成员。比如上面的示例中的类Boss。除了类之外还可以声明别的,比如变量、常量、函数,甚至嵌套命名空间:

#include 

namespace X {
    int a; // 变量
    int b = 9;  // 带初始化的变量
    void c() { // 函数
        std::cout << "我是函数c" << std::endl;
    }
    const int d = 6; // 常量
    namespace Y { // 嵌套命名空间
        int b = 10;
    }
}

int main() {
    X::a = 1;
    X::c();
    std::cout << X::a << std::endl;
    std::cout << X::b << std::endl;
    std::cout << X::d << std::endl;
    std::cout << X::Y::b << std::endl;
    return 0;
}

编译并运行:

$ g++ main.cpp
$ ./a.out 
我是函数c
1
9
6
10

使用命名空间

除了在使用命名空间时通过形如X::aX::Y::b这种用::连起来的形式直接访问命名空间成员外,还可以通过关键字using namespace加命名空间名称的方式,引入命名空间下的所有命名空间成员到当前的作用域,或者直接using加命名空间加::号加命名空间成员,直接在当前作用域引入该成员(只引入命名空间成员能防止因引入所有命名空间成员导致的标识符污染问题),这样使用时可以省略::符号。比如:

* using整个命名空间:

#include 

using namespace std;

int main() {
    cout << "你好" << endl;
    return 0;
}

* using只引入命名空间成员:

#include 

using std::cout;
using std::endl;

int main() {
    cout << "你好" << endl;
    return 0;
}

* using可以在特定的作用域里使用,比如只在函数作用域内使用:

#include 

void say() {
    using namespace std;
    cout << "你好" << endl;
}

int main() {
    say();
    return 0;
}

匿名命名空间

可以声明没有命名空间名字的命名空间,这种叫做匿名命名空间。匿名命名空间的命名空间成员作用范围是在当前编译单元(编译器处理的对象称为编译单元,通常是一个源代码文件。我们可以将程序的所有的源代码放在一个文件中,或者都定义在头文件里然后通过include在预处理阶段包含到同一个文件内,让编译器处理,这样这个程序只有一个编译单元。或者简单地说,一次g++命令处理的所有文件组合成的就是一个编译单元)。比如:

#include 

namespace {
    void say() {
        using namespace std;
        cout << "你好" << endl;
    }
}

int main() {
    say(); // 这里会执行上面的 say 函数
    return 0;
}

匿名命名空间可以用于声明、定义仅在一个编译单元里有效的成员,类似C语言的static关键字起到的作用。

命名空间可以拆开

例如:

#include 

namespace X {
    void say_1() {
        using namespace std;
        cout << "你好 1" << endl;
    }
}

namespace X {
    void say_2() {
        using namespace std;
        cout << "你好 2" << endl;
    }
}

int main() {
    X::say_1();
    X::say_2();
    return 0;
}

编译运行:

$ g++ main.cpp 
$ ./a.out 
你好 1
你好 2

设置别名

如果有需要给命名空间指定一个别名,C++也支持。比如下面演示了如何给命名空间X::Y::Z指定别名A,然后使用A就像使用X::Y::Z一样。

#include 

// 在任何地方定义的命名空间,X::Y::Z
namespace X {
    namespace Y {
        namespace Z {
            void say() {
                using namespace std;
                cout << "你好" << endl;
            }
        }
    }
}

// 下面给命名空间 X::Y::Z 设置别名,A,然后使用 A
namespace A = X::Y::Z;

int main() {
    A::say();
    return 0;
}

编译并运行:

$ g++ main.cpp
$ ./a.out
你好

关键字using除了可以用来使用命名空间成员外,也可以给类型指定别名。例如:

#include 

using A = int;

int main() {
    A x = 567;
    std::cout << x << std::endl;
    return 0;
}

运行输出

$ g++ main.cpp
$ ./a.out 
567

参考材料

  1. C++命名空间 namespace的作用和使用解析
  2. 【C++】命名空间(namespace)详解
  3. C++中的namespace
  4. C++ 之namespace常见用法
  5. 为什么尽量不要使用using namespace std?
  6. c++ 的namespace及注意事项
  7. C++(一)C++中using namespace std 的作用
  8. C++头文件定义类的方法
  9. C++教程,一文详解C++命名空间
  10. C++ 命名空间
  11. C++命名空间(名字空间)详解
  12. C++语言用户标识符的合法规则
  13. C++11使用using定义别名(替代typedef)
  14. C++名字空间/C++命名空间

你可能感兴趣的:(IT技术相关,namespace,c++,编程语言)