15.命名空间

15.面向对象-封装-命名空间


15.1 知识点

命名空间是什么

  • 命名空间是为了防止命名冲突而设计的一种机制
  • 在声明全局变量、函数、类等内容时,不能在同一作用域中重名
  • 有了命名空间,可以在不同命名空间中声明同名内容
  • 命名空间就像是一个工具包,在其中可以放置一些相同名字的工具

命名空间的声明和定义

  • .h.cpp 中都可以声明和定义命名空间

    • 如果在 .cpp 中声明和定义,只能在该文件中使用,无法在其他文件中使用

    • 如果在 .h 中声明,可通过引用头文件在其他文件中使用

    • 定义可以直接在 .h 中,也可以分开在 .cpp 中;建议 .h 中声明,**.cpp** 中定义

    • 注意:在 .h 中定义全局变量需要加上 extern 关键字

      • extern 在此处的作用是:在命名空间中写全局变量时,如果变量要在多个文件中共享使用,就必须在头文件中写 extern 声明,在 .cpp 文件中写实际定义

声明语法示例:

namespace 命名空间名
{
    变量
    函数
    类
    等等
}
  • 注意:

    • .h 中声明全局变量需加上 extern
    • 如果在 .h 中声明、在 .cpp 中实现,命名空间中的类方法定义需使用 命名空间名::类名::方法 的形式

.cpp 中声明和定义:

//如果在cpp中声明和定义 那么只能在该cpp中进行使用
namespace MyNameSpace
{
    int i = 10;
    void test()
    {
        cout << "MyNameSpace命名空间中的test方法" << endl;
    }

    class Test
    {
    public:
        void test()
        {
            cout << "Test类中的test方法" << endl;
        }
    };
}

在头文件声明 (.h),在 .cpp 中定义:

#pragma once
#include <iostream>
using namespace std;

namespace MyTool
{
    extern int i;
    void test();

    class Test
    {
    public:
        int i;
        void test();
    };
}

namespace Math
{
    extern float PI;

    int add(int a, int b);

    class Test
    {
    public:
        int i;
        void test();
    };
}
#include "MyTool.h"

// 1. 通过 namespace 进行包裹定义
namespace MyTool
{
    int i = 10;
    void test()
    {
        cout << "MyTool命名空间中的test方法" << endl;
    }

    // 类当中的内容定义只能使用命名空间展开方式
}

// 2. 通过 命名空间名::成员 形式指定定义
void MyTool::Test::test()
{
    cout << "MyTool命名空间中的Test类中test方法" << endl;
}

float Math::PI = 3.1415926f;

int Math::add(int a, int b)
{
    return a + b;
}

void Math::Test::test()
{
    cout << "Math命名空间中的Test类中test方法" << endl;
}

命名空间的使用

  • 使用命名空间,除了引用头文件外,还需指定内容来自哪个命名空间

  • 基本语法:命名空间名::变量名命名空间名::函数名命名空间名::类名

//MyNameSpace是当前cpp脚本中自己定义的命名空间 所以可以直接用不用#include

// 使用 MyNameSpace 中的变量和方法
cout << MyNameSpace::i << endl;      // 10
MyNameSpace::test();                // MyNameSpace命名空间中的test方法

// 使用 MyNameSpace 中的类
MyNameSpace::Test t;
t.test();                           // Test类中的test方法

// 使用 MyTool 和 Math(需 #include "MyTool.h")
cout << MyTool::i << endl;          // 10
MyTool::test();                     // MyTool命名空间中的test方法
MyTool::Test t2;
t2.test();                          // MyTool命名空间中的Test类中test方法

cout << Math::PI << endl;           // 3.14159
cout << Math::add(5, 2) << endl;    // 7
Math::Test t3;
t3.test();                          // Math命名空间中的Test类中test方法
  • 或者在文件顶部使用 using namespace 命名空间名;,调用时可省略前缀

  • 注意:不同命名空间中若有同名内容,需明确指定命名空间以免冲突

using namespace MyTool;

cout << i << endl;   // 10
test();              // MyTool命名空间中的test方法
Test t4;
t4.test();           // MyTool命名空间中的Test类中test方法

//但是 假如using namespace Math; Math里又有Test这个类 
//就会分不清Test是哪个命名空间的 导致报错 就得明确指定了

命名空间可以分散式声明

  • 在多个文件中声明同一个命名空间,它们代表同一个空间,成员不得重名。
  • 比如声明一个MyTool2.h,里面也声明namespace MyTool是可以的,他和在MyTool.h声明的MyTool命名空间是一个命名空间。注意成员不要重名就行。

MyTool2.h

#pragma once
namespace MyTool
{
    extern float f;
    void test2();

    namespace Inner
    {
        namespace Inner2
        {
            extern float f;
            void InnerTest();
        }
    }
}

MyTool2.cpp

#include "MyTool2.h"
#include <iostream>
using namespace std;

float MyTool::f = 2.2f;

void MyTool::test2()
{
    cout << "另一个文件中声明的命名空间内容" << endl;
}

使用:

cout << MyTool::f << endl;    // 2.2
MyTool::test2();              // 另一个文件中声明的命名空间内容

嵌套命名空间

  • 命名空间可无限嵌套声明,类似嵌套类
#pragma once
namespace MyTool
{
    extern float f;
    void test2();

    namespace Inner
    {
        namespace Inner2
        {
            extern float f;
            void InnerTest();
        }
    }
}
float MyTool::Inner::Inner2::f = 5.5f;

void MyTool::Inner::Inner2::InnerTest()
{
    cout << "MyTool::Inner::Inner2::InnerTest" << endl;
}
cout << MyTool::Inner::Inner2::f << endl;  // 5.5
InnerTest();                               // MyTool::Inner::Inner2::InnerTest

命名空间别名

  • 基本语法:namespace 自定义名 = 命名空间名;
  • 作用:简化复杂的命名空间名
//比如在最上面 namespace customName = MyTool::Inner::Inner2; 整个文件都能使用
// 文件顶部
namespace customName = MyTool::Inner::Inner2;

customName::InnerTest();
// 函数内别名,仅在函数作用域内有效
namespace customName2 = MyTool::Inner::Inner2;
customName2::InnerTest();

匿名命名空间

  • 匿名命名空间让变量、函数、类的作用域仅限当前 .cpp 文件

  • 防止与其他文件冲突,实现更好的封装,是 static 的现代替代品

  • 只能在当前文件中可见,允许在多个 .cpp 文件中定义同名函数或变量,不会有重定义问题

  • 注意:不能在头文件中使用匿名命名空间

// MyTool.cpp
namespace
{
    int secret = 10;
}

void print()
{
    cout << secret << endl;
}

// MyTool2.cpp
namespace
{
    int secret = 99;
}

void print2()
{
    cout << secret << endl;
}

// 使用

//顶部下 主函数前
extern void print();
extern void print2();

//调用
print();   // 10,打印 MyTool.cpp 的匿名命名空间的 secret
print2();  // 99,打印 MyTool2.cpp 的匿名命名空间的 secret

15.2 知识点代码

MyTool.h

#pragma once
#include <iostream>
using namespace std;
namespace MyTool
{
    extern int i;
    void test();

    class Test
    {
    public:
        int i;
        void test();
    };
}

namespace Math
{
    extern float PI;

    int add(int a, int b);

    class Test
    {
    public:
        int i;
        void test();
    };
}

MyTool.cpp

#include "MyTool.h"

//1.通过namespace进行包裹 定义
namespace MyTool
{
    int i = 10;
    void test()
    {
        cout << "MyTool命名空间中的test方法" << endl;
    }

    //类当中的内容定义 只能使用第二种方式
}

//2.通过类似类一样 命名空间名::变量、函数、类相关 去指定定义
void MyTool::Test::test()
{
    cout << "MyTool命名空间中的Test类中test方法" << endl;
}

float Math::PI = 3.1415926f;

int Math::add(int a, int b)
{
    return a + b;
}

void Math::Test::test()
{
    cout << "Math命名空间中的Test类中test方法" << endl;
}

namespace
{
    int secret = 10;
}

void print()
{
    cout << secret << endl;
}

MyTool2.h

#pragma once
namespace MyTool
{
    extern float f;
    void test2();

    namespace Inner
    {
        namespace Inner2
        {
            extern float f;
            void InnerTest();
        }
    }
}

MyTool2.cpp

#include "MyTool2.h"
#include <iostream>
using namespace std;
float MyTool::f = 2.2f;

void MyTool::test2()
{
    cout << "另一个文件中声明的命名空间内容" << endl;
}

float MyTool::Inner::Inner2::f = 5.5f;

void MyTool::Inner::Inner2::InnerTest()
{
    cout << "MyTool::Inner::Inner2::InnerTest" << endl;
}

namespace
{
    int secret = 99;
}

void print2()
{
    cout << secret << endl;
}

Lesson15_面向对象_封装_命名空间.cpp

#include <iostream>
#include "MyTool.h"
#include "MyTool2.h"
using namespace std;
using namespace MyTool;
//using namespace Math;
using namespace MyTool::Inner::Inner2;
namespace customName = MyTool::Inner::Inner2;

extern void print();
extern void print2();

//如果在cpp中声明和定义 那么只能在该cpp中进行使用
namespace MyNameSpace
{
    int i = 10;
    void test()
    {
        cout << "MyNameSpace命名空间中的test方法" << endl;
    }

    class Test
    {
    public:
        void test()
        {
            cout << "Test类中的test方法" << endl;
        }
    };
}

int main()
{
    #pragma region 知识点一 命名空间是什么
    //命名空间是为了防止命名冲突而设计的一种机制
    //我们在声明全局变量、函数、类等等内容时,是不能够在同一作用域中重名的
    //而有了命名空间,我们可以在不同命名空间中声明同名内容
    // 
    //命名空间就像是一个工具包,在其中可以放置一些相同名字的工具
    #pragma endregion

    #pragma region 知识点二 命名空间的声明和定义
    //在哪里声明和定义:
    //命名空间在.h和.cpp中都可以声明和定义
    //1.如果在.cpp中声明和定义就只能在该文件中使用,无法在其他文件中使用
    //2.如果在.h中声明,那么可以通过引用头文件的形式,在其他文件中使用
    //	定义可以直接在.h中也可以分开在.cpp中,建议在.h中声明,在.cpp中定义
    //	注意:如果在.h中定义全局变量,需要加上extern关键字
    //	(extern关键字在此处的作用是 在命名空间中写全局变量时,
    //   如果变量要在多个文件中共享使用,就必须在头文件中写 extern 声明,在 .cpp 文件中写实际定义)

    //声明语法:
    /*namespace 命名空间名
    {
        变量
        函数
        类
        等等
    }*/

    //注意:
    //1..h中声明 全局变量需要加上 extern
    //2..h中声明 .cpp中实现 那么如果是命名空间中的类中的内容需要定义 需要通过 命名空间名::类名::方法定义
    #pragma endregion

    #pragma region 知识点三 命名空间的使用
    //想要使用命名空间,除了引用头文件外
    //1.需要指定使用内容来自哪个命名空间
    // 	基本语法:
    //  命名空间名::变量名(函数名或类名等等)

    //MyNameSpace是当前cpp脚本中自己定义的命名空间
    // 
    //使用其中的变量和方法
    cout << MyNameSpace::i << endl;//10
    MyNameSpace::test();//MyNameSpace命名空间中的test方法

    //使用其中的类
    MyNameSpace::Test t;
    t.test();//Test类中的test方法

    //MyTool和Math是声明在MyTool.h中的命名空间 需要#include "MyTool.h"
    cout << MyTool::i << endl;//10
    MyTool::test(); //MyTool命名空间中的test方法
    MyTool::Test t2;
    t2.test();//MyTool命名空间中的Test类中test方法

    cout << Math::PI << endl;//3.14159
    cout << Math::add(5, 2) << endl;//7
    Math::Test t3;
    t3.test();//Math命名空间中的Test类中test方法



    //2.或者可以在顶端使用 using namespace 命名空间名;
    //	这样可以在使用时省略前面的内容
    //	注意:需要注意使用不同命名空间时,其中有同名内容问题时,需要明确来自于哪个命名空间

    //比如在最前面using namespace MyTool;
    cout << i << endl;//10
    test(); //MyTool命名空间中的test方法
    Test t4;
    t4.test();//MyTool命名空间中的Test类中test方法
    //但是 假如using namespace Math; Math里又有Test这个类 
    //就会分不清Test是哪个命名空间的 导致报错 就得明确指定了


    #pragma endregion

    #pragma region 知识点四 命名空间可以分散式声明
    //如果我们在多个文件中声明同一个命名空间
    //那么他们代表的是一个命名空间

    cout << MyTool::f << endl;//2.2
    MyTool::test2();//另一个文件中声明的命名空间内容
    #pragma endregion

    #pragma region 知识点五 嵌套命名空间
    //命名空间可以类似嵌套类一样无限嵌套声明
    cout << MyTool::Inner::Inner2::f << endl;//5.5
    InnerTest();//MyTool::Inner::Inner2::InnerTest

    #pragma endregion

    #pragma region 知识点六 命名空间别名
    //基本语法:
    //namespace 自定义名 = 命名空间名
    //作用:
    //可以简化复杂的命名空间名

    //比如在最上面 namespace customName = MyTool::Inner::Inner2; 整个文件都能使用
    customName::InnerTest();

    //下面这个别名在函数内 只在函数语句块有用
    namespace customName2 = MyTool::Inner::Inner2;
    customName2::InnerTest();

    #pragma endregion

    #pragma region 知识点七 匿名命名空间
    //匿名命名空间让变量、函数、类的作用域只在当前 .cpp 文件中
    //防止和其他文件冲突,实现更好的封装,是 static 的现代替代品
    //只在当前文件中可见,允许在多个.cpp文件中定义同名函数或变量时,不会有重定义问题

    //注意:不能在头文件中使用匿名命名空间

    print();//10 打印MyTool.cpp的匿名命名空间的secret变量
    print2();//99 打印MyTool2.cpp的匿名命名空间的secret变量

    #pragma endregion
}

15.3 练习题

命名空间声明定义时应该注意些什么?

  1. 虽然在 .h.cpp 文件中都能声明和定义命名空间,但一般建议在头文件(.h)中进行声明,在源文件(.cpp)中进行定义。因为如果只在cpp中声明定义,无法在其他地方使用了。
  2. 在头文件中声明全局变量时,需要加上 extern,并在对应的源文件中进行定义,否则其他翻译单元无法使用。
  3. 若在同一文件中使用多个命名空间且它们存在同名内容,using namespace 会引发冲突。此时应明确指出所使用成员所属的命名空间,不要省略前缀。
  4. 匿名命名空间不要在.h中声明和定义,它的作用是类似集群式 static,只能在.cpp中声明和定义,可以使其中的内容只在当前cpp中生效。

命名空间冲突示例及修改

下面代码演示了在同时 using namespace A;using namespace B; 时对同名变量 x 的冲突:

namespace A {
    int x = 5;
}

namespace B {
    int x = 10;
}

using namespace A;
using namespace B;

int main() {
    std::cout << x << std::endl;
}

该代码无法通过编译,因为编译器无法判断 x 属于 A::x 还是 B::x。应改为显式指定命名空间:

namespace A {
    int x = 5;
}

namespace B {
    int x = 10;
}

int main() {
    std::cout << A::x << std::endl;   // 5
    std::cout << B::x << std::endl;   // 10
}

15.4 练习题代码

Lesson15_练习题.cpp

#include <iostream>
namespace A {
    int x = 5;
}

namespace B {
    int x = 10;
}

using namespace A;
using namespace B;
using namespace std;

int main()
{
    #pragma region 练习题一
    //请简单总结命名空间声明定义时应该注意些什么?
    // 
    //1.虽然 cpp 和 .h以及.cpp 中都能声明和定义
    //  但是我们一般建议 分开在.h和.cpp中进行声明和定义,因为如果只在cpp中声明定义,无法在其他地方使用了
    //2.在.h中声明时,全局变量需要加上 extern,并且需要在cpp中进行定义
    //3.在同一文件中使用多个命名空间时,如果他们其中有同名的内容,在使用 using namespace 时需要注意同名冲突
    //	我们一般需要明确使用的内容来自哪个命名空间,不要去省略
    //4.匿名命名空间不要在.h中声明和定义,它的作用是类似集群式 static,只能在.cpp中声明和定义
    //	可以是其中的内容只在当前cpp中生效
    #pragma endregion

    #pragma region 练习题二
    /*namespace A {
        int x = 5;
    }

    namespace B {
        int x = 10;
    }

    using namespace A;
    using namespace B;

    int main() {
        std::cout << x << std::endl;
    }
    上述代码能否通过编译?如果不能,哪里错了?如何修改?*/

    //不能 需要明确哪个命名空间
    cout << A::x << endl;//5
    cout << B::x << endl;//10
    #pragma endregion
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏