啃书《C++ Primer Plus》之 C++ 名称空间1

笔者正在学习C++语言,啃书系列将会持续更新,希望可以同大家一起学习,一起进步。如果文章有帮助的话,记得点赞、收藏、关注一条龙哦。

系列文章:
啃书《C++ Primer Plus》之 C++ 函数指针
啃书《C++ Primer Plus》之 C++ 名称空间2


名称空间是C++语言不可或缺的特色,在名称管理上有着举足轻重的作用。这篇文章就来总结一下名称空间这个知识点。
参考资料:《C++ Primer Plus》
本文思维导图如下:
在这里插入图片描述
由于内容较多,该节拆分成两部分,本文为上部分,内容到名称空间的冲突。下部分见《名称空间2》


名称空间的作用与使用规则

名称空间解决的问题

在名称空间之前,C和C++语言通过局部、全局、静态等属性限制一个变量或函数的作用域,同时限定其名称作用范围。但是这样的解决方式在面临大项目时还是会出现问题,不同文件可能会定义相同名称的全局变量,这样如果一个文件同时引用了包含同一名称全局变量的两个文件,就会造成名称空间问题。
名称空间的引入,就是为了解决这类问题,为名称提供一个名称空间,不同的名称空间,可以包括相同的名称,彼此互不干涉。
另外,上文提到的全局变量,在引入名称空间的机制后,也有了对应的名称空间——全局名称空间(global namespace)。这个名称空间包含了所有的全局变量。

名称空间特点与成员的声明定义

名称空间特点

名称空间的声明与定义,需要用到关键字: namepace,写法如下:

namespace 名称空间名称{
.
.
.
} 

名称空间的作用域是全局。也就是说,对于整个工程,某个文件A引入了包含名称空间S的文件B,那么文件A便可以使用名称空间S。常见的,我们引用 iostream 其中就包含了 std这个名称空间。(这个提点在嵌套时是例外的,在下文会谈到)

名称空间是开放的。 这意味着可以加入一个名称到已有的名称空间中,语法同上。这也就代表着,当编译器检测到上文的语法时,如果已经存在这个名称空间,则会将这个代码块中的内容添加到已有的名称空间中,否则就创建一个新的名称空间。

需要特别注意的是:
名称空间不能位于代码块中!
名称空间不能位于代码块中!
名称空间不能位于代码块中!
名称空间不能位于代码块中!

’写名称空间内容的时候,一定要在代码快之外,不要放在任何的函数等的代码块中。

名称空间内容

在名称空间大括号中可以定义结构,类,变量,函数等,他们都属于这个名称空间。
需要注意的是,结构,类,函数这些内容的声明与定义通常是分开的,而在命名空间中,声明和定义也可以分开写。

我们知道,在c++语言中,声明和定义是两个不同的概念,声明告诉编译器格式,而定义是具体实现。
通常,将成员定义在名称空间中的代码放在 .cpp文件中,而将成员声明在名称空间的代码放在对应的头文件中。

举个栗子:

//test.h
#ifndef TEST_H_INCLUDED
#define TEST_H_INCLUDED
namespace test
{
    int k = 10;
    int f(int,int);
    class A
    {
    public:
        int getA();
        void setA(int);
    private:
        int a;
    };
}
#endif // TEST_H_INCLUDED

//test.cpp
#include "test.h"
namespace test
{
    int f(int a,int b)
    {
        return a + b;
    }
    int A::getA()
    {
        return this->a;
    }
    void A::setA(int a)
    {
        this->a = a;
    }
}

这个程序中,定义了名称空间 test 。在头文件中,向 test 空间内放入了函数 f 以及类 A 的声明。在对应的 .cpp文件中对他们进行了定义。


使用名称空间

定义了名称空间并向其中填写内容后,可以在代码块中使用这个名称空间中的名称。这里介绍三个使用方式。

作用域解析符 “::”

首先,作用域解析符 "::"提供了访问名称空间中名称的方式。用法如下:

名称空间::某名称;

功能类似于结构或类对象访问其成员用的 “.”,相当于表明调用某个名称空间下的某名称。
常见的,调用 std 名称空间下的 cout、endl 关键字,例如:

...
std::cout << "hello world!" << std::endl;
...

除了作用域解析符的访问方式,C++提供了两种机制来简化对名称的使用,下面来介绍他们。

using声明

在这里,需要引入一个新的关键字:using,那么它的含义就如同其字面意思:使用。
使用什么呢?using声明提供了一种使用名称空间下名称的方式。它的用法如下:

using 名称空间::名称;

using声明将名称添加到所属的声明区域中。如果这条语句出现在某个代码块内,那么只在这个代码块中可以直接使用这个名称。如果这条语句出现在全局,所有在这之后的语句都可以直接使用这个名称。

用using声明实现上面的代码:

...
using std::cout;
using std::endl;
cout << "hello world!" << endl;
...

nice !!~~~

using编译指令

不同于using声明使一个名称可用。using编译指令可使名称空间中所有名称可用。它的写法如下:

using namespace 名称空间;

是不是非常的熟悉呢,在编写c++程序的时候,常常会在文件引用完头文件后,加上这么一句:

using namespace std;

这句话的意思就是,使用using编译指令,使用所有 std 名称空间的名称。也因此,我们才可以在程序中直接的使用 cin cout endl string 等关键字。
再来用using编译指令实现上面的代码:

using namespace std;
...
cout << "hello world" << endl;
...

名称空间的冲突

名称空间的使用并非能避免所有的名称冲突问题,如果使用不当,仍会造成问题。

using编译指令的冲突问题

using编译指令引入一个名称空间中所有的名称,其中不乏有一段程序中本身就包含的名称。另外前面还提到,不同的名称空间可以允许相同的名称存在,但是当同一段程序同时使用using编译指令引用两个包含相同名称的名称空间时,问题就会出现。

名称空间之间的冲突

当使用using编译指令使用两个名称空间时,若其中包含有相同的名称,则编译器会认为这个名称具有两义性,从而报错

名称空间与原有名称的冲突

当引入的名称空间中的名称与原有的变量等名称像冲突时,参考如下代码:

#include <iostream>
using namespace std;
namespace A
{
    int k = 10;
    int l = 9;
    int f(){return 10086;}
    struct Node{};
    class Tree{};
}
int f();
struct Node{};
class Tree{};
int l = 99;
int main()
{
    int k = 20;
    using namespace A;
    cout << k << endl;
    cout << l << endl;
    cout << f() << endl;
    Node node;
    Tree tree;
    return 0;
}
int f(){return 10010;}

编译器给出如下错误信息:在这里插入图片描述
提示全局变量l、函数f、结构Node、类Tree含义模糊

可知,新引入的名称覆盖了原有的局部变量的名称,并和已经存在的函数、全局变量、结构、类等产生冲突。

要解决这些问题,在使用特定的名称时,可以使用域名解析符来使用名称空间中的名称。

using声明的名称冲突

上面说明了using编译指令引入全部名称所带来的问题,那么单个引入名称的using声明会不会造成同样的问题呢?
将上面的代码替换成:

#include <iostream>
using namespace std;
namespace A
{
    int k = 10;
    int l = 9;
    int f(){return 10086;}
    struct Node{};
    class Tree{};
}
int f();
struct Node{};
class Tree{};
int l = 99;
int main()
{
    int k = 20;
    using A::k;
    using A::l;
    using A::f;
    using A::Node;
    using A::Tree;
    cout << k << endl;
    cout << l << endl;
    cout << f() << endl;
    Node node;
    Tree tree;
    return 0;
}
int f(){return 10010;}

此时,编译器告知:在这里插入图片描述错误仅有一个,就是变量k已经存在,不能引入,而对其它成员,则没有问题。

删除引入k,运行程序,得到如下结果:
在这里插入图片描述
最后得出结论:using声明的名称冲突与using编译指令不同。对于已经储存在的局部变量,会产生多重定义的错误,对于全局变量,函数,结构,类等则会进行覆盖。

猜你喜欢

转载自blog.csdn.net/wayne_lee_lwc/article/details/104961419