首页 > 编程笔记 > C++笔记 阅读:46

C++ namespace命名空间的用法(非常详细,新手必看)

C++ 大型系统开发中,因为多人参与共同开发,所以往往难以保证函数、变量或者类的唯一性。

例如,张三定义了一个名为 Student 的类,而李四在不知情的情况下也定义了一个名为 Student 的类。这两个不同的 Student 类在同一个系统中会导致命名冲突,进而引发编译错误。

为了解决这个问题,C++ 提供了命名空间(namespace)来组织和管理程序中的各种名字(如函数名、变量名、类名等)。在实际开发中,通常会根据不同的模块定义不同的命名空间,以确保各个模块内部的名字不会相互冲突,命名空间对于实现模块包装起到良好的作用。

如果把函数看作一个箱子,那么命名空间则可以看作一个仓库。我们可以把相关功能的多个函数放入同一个仓库中,这样即使在其他仓库中有相同名字的函数,它们也会互不影响。也就是说,不同命名空间中的同名函数、变量等可以同时存在,互不冲突。

例如,张三可以在命名空间 Zhangsan 中定义 Student 类,而李四可以在命名空间 Lisi 中定义 Student 类,这样两个同名的类就不会发生冲突。

C++ 定义命名空间的语法格式如下:
namespace 命名空间名
{
    // 命名空间内的声明和定义
};
其中,namespace 关键字表示命名空间的开始,其后的命名空间名通常是一个易于识别并能表达命名空间含义的名词。定义命名空间后,可以在其中进行各种声明和定义,这些声明和定义都属于这个命名空间。

下面的示例展示了如何使用命名空间来解决前文张三和李四之间的名字冲突问题:
// 命名空间 Zhangsan
namespace Zhangsan
{
    // 命名空间 Zhangsan 中的 Student 类
    class Student
    {
    public:
        int nIndex;    // 索引
        int nAge;      // 年龄
    };
}

// 命名空间 Lisi
namespace Lisi
{
    // 命名空间 Lisi 中的 Student 类
    class Student
    {
    public:
        int nIndex;
        string strName;    // 姓名
    };
}

// 匿名命名空间
// 如果没有说明在哪一个具体的命名空间,则默认在匿名命名空间
class Student // 匿名命名空间中的 Student 类
{
public:
    int nIndex;
    bool bMale;    // 性别
};

class Teacher // 匿名命名空间中的 Teacher 类
{
    // ...
};
从以上代码中可以看到,我们定义了三个不同实现但同样名为 Student 的类。这三个同名的类并没有产生命名冲突,如下图所示。


图 1 命名空间的封装功能

这是因为这三个 Student 类分别属于不同的命名空间,命名空间的不同确保了它们之间不会产生冲突,就像小陈家有电视机,小王家也有电视机,虽然它们的名字相同,但实际上是两台不同的电视机,互不影响。

在一个程序中,如果有多个同名的类,又该如何区分它们呢?

正如我们前文提到的“小陈家有电视机,小王家也有电视机”,我们可以通过在这些同名的类之前加上相应的命名空间来进行区分,例如:
// 定义一个类型为 Zhangsan::Student 的变量
// 这时使用的是 Zhangsan 命名空间下的 Student 类
Zhangsan::Student zStudent;
zStudent.nAge = 14;
// 定义一个类型为 Lisi::Student 的变量
// 这时使用的是 Lisi 命名空间下的 Student 类
Lisi::Student lStudent;
lStudent.strName = "Chen";
// 定义一个类型为 ::Student 的变量
// 如果类前没有指明命名空间,则默认为全局命名空间
// ::lStudent 等同于 Student
::Student gStudent;
gStudent.bMale = true;
在使用命名空间时,可以在命名空间之后使用“::”域操作符引出其中的名字。

例如,如果我们希望使用在 Zhangsan 命名空间中的 Student 类来定义一个变量,我们可以在 Zhangsan 命名空间之后使用“::”域操作符引出 Student 类来定义变量 zStudent。这个对象拥有 m_nAge 的成员变量,我们可以直接给她们赋值。

类似的,我们也可以用相同的方式来访问 Lisi 命名空间中的 Student 类。

特别地,如果“::”之前没有显式地给出一个命名空间,这意味着它属于全局命名空间,也称为匿名命名空间。匿名命名空间是 C++ 的默认命名空间,如果某个名字不属于任何给定的命名空间,它就属于匿名命名空间。例如,上述例子中的第三个 Student 类就属于匿名命名空间,我们可以使用 ::StudentStudent 来引用它。

在实际开发中,为了简化代码,并不总是需要显式地指明某个名字所属的命名空间,可以使用 using namespace 关键字来指明编译时默认查找的命名空间,这样引入了该命名空间下的所有名字。

例如,如果在源文件中添加了using namespace Zhangsan;语句,就表示将 Zhangsan 这个命名空间作为默认查找的命名空间。当编译器遇到某个名字时,不仅会在默认的匿名命名空间中查找,还会在 Zhangsan 这个新引入的命名空间中查找:
例如:
// ...
// 引入命名空间 zhangsan
using namespace Zhangsan;

int main()
{
    // 名字冲突,在 Zhangsan 命名空间和匿名命名空间中都有 Student 类
    Student zStudent;
    // 明确的名字,显式地指明了 Student 所属的命名空间为 Lisi
    Lisi::Student lStudent;

    // 明确的名字,因为只有匿名命名空间中有 Teacher 类的定义
    Teacher zTeacher;

    return 0;
}
在这段代码中,使用 using namespace 关键字引入了命名空间 Zhangsan,连同默认的匿名命名空间一起作为查找名字的范围。当编译器查找 Student 类的定义时,会发现在匿名命名空间和命名空间 Zhangsan 中都有 Student 的定义,这时就会引发名字冲突的编译错误。

在这种情况下,只有使用“::”符号明确地指明 Student 所属的命名空间才能解决问题。而对于 Teacher 类,由于在命名空间中只有唯一定义的类,因此可以直接使用类名定义变量。当然,我们也可以给 Teacher 类额外加上命名空间的前缀,以便更明确地表明这个名字属于哪个命名空间。

使用 using namespace 关键字引入某个命名空间时,可以省去在名字前加上命名空间名的工作,这样在一定程度上可以提高编程效率,然而,这样做会引入该命名空间中的所有名字,有可能会引起命名冲突。因此,使用这种方式是应谨慎。

相关文章