C++ 中的 namespace 使用和注意事项

上篇文章中提到了 C++ 中的 namespace,出现了一些奇怪的现象,这里我们以三段程序引出我们要讨论的问题。

代码段1:

为了避免命名空间中的变量和当前作用域中的变量同名发生冲突,我们一般会这样使用命名空间:

#include <iostream>
#include <string>

using namespace std;

namespace first
{
int a = 10;
}

int main()
{
    int a = 100;
    cout<<a<<endl;
    {
        using first::a;
        cout<<a<<endl;
    }
    cout<<a<<endl;

    return 0;
}

结果为:

100
10
100

但是如果在上边的代码中添加一句话,又会出现不一样的情况。

代码段2:

#include <iostream>
#include <string>

using namespace std;

namespace first
{
int a = 10;
}

int main()
{
    int a = 100;
    cout<<a<<endl;
    {
        using first::a;
        cout<<a<<endl;
    }

    cout<<a<<endl;
    using first::a;
    cout<<a<<endl;

    return 0;
}

结果提示报错:

main.cpp:21: error: 'a' is already declared in this scope

main.cpp:21: error: redeclaration of 'int first::a'

这意味着后边的 using first::a 使用的是不对的。

在上篇文章中,我们同样提到命名空间可以通过三种方式进行使用,如果我们换一种方式使用命名空间,结果会不会有点不一样。

代码段3:

#include <iostream>
#include <string>

using namespace std;

namespace first
{
int a = 10;
}

int main()
{
    int a = 100;
    cout<<a<<endl;
    {
        using namespace first;
        cout<<a<<endl;
    }

    cout<<a<<endl;
    using namespace first;
    cout<<a<<endl;

    return 0;
}

结果为:

100
100
100
100

上边的代码虽然没有报错,但很明显并不是我们想要的结果。那么 C++ 中的 namespace 到底是怎么使用的?

使用方式

  • 直接指定 namespace,namespace::var = value;
  • 使用 using namespace::var,using namespace::var;
  • 使用 using namespace spacename,如 using namespace std;

注意事项

  • 使用 namespace::var = value 或 using namespace::var 的情况下,需要填写 var 所处的绝对路径
  • 使用 using namespace::var,会将变量 var 直接引入到当前作用域,因此不允许同名
  • 使用 using namespace spacename 能够加载整个命名空间,从而可以使用整个命名空间定义的内容(变量,函数,数据类型,命名空间等)
  • using namespace spacename 相当于在当前作用域中将 namespace 中的所有内容全部导入 “global" 命名空间中,如果当前作用域中存在同名的局部变量,则该局部变量会覆盖导入的内容
  • using namespace::var 存在作用域和指令顺序性,允许使用多个 namespace,但如果多个 namespace 中存在相同的符号时,并不会发生覆盖行为,而会显示 ambigious,此时可以通过补全使用内容的路径名来解决
  • 对于匿名的 namespace,编译器会为每个不同编译单元生成的不同的 namespce 并使用,可以通过匿名空间实现类似于 static 的符号外部不可见性

namespace 实质

namespace 的实质就是在编译期结束后给内容加前缀,可以利用 Linux 看生成 main.s,可以找到包含 spacename 的符号。

namespace 实例

三种使用方式

#include <iostream>
#include <string>

using namespace std;

namespace first
{
int a = 10;
}

int main()
{
//    cout<<a<<endl;
    cout<<first::a<<endl;
    {
//        cout<<a<<endl;
        using first::a;
        cout<<a<<endl;
    }

//    cout<<a<<endl;
    using namespace first;
    cout<<a<<endl;

    return 0;
}

结果为:

10
10
10

上面的代码中使用了三种方式使用 first 中的 a 变量。如果我们试图添加上边注释掉的代码,都会提示:

error: 'a' was not declared in this scope

使用绝对路径

#include <iostream>
#include <string>

using namespace std;

namespace first
{
int a = 10;
namespace second
{
int a = 20;
}
}

int main()
{
    cout<<first::a<<endl;
    cout<<first::second::a<<endl;

    return 0;
}

结果为:

10
20

using namespace spacename

#include <iostream>
#include <string>

using namespace std;

namespace first
{
int a = 10;
}

int a = 30;

int main()
{
    int a = 40;
    cout<<a<<endl;

    cout<<::a<<endl;
    {
        using first::a;
        cout<<a<<endl;
    }
    cout<<a<<endl;
    using namespace first;
    cout<<a<<endl;

    return 0;
}

结果为:

40
30
10
40
40

这里可以用一个图来表示一下:

  • 上边结果第一个 40,显示的是 local 中的 a
  • 上边结果第二个 30,显示的是 global 中的 a,:: 前边如果没有命名空间的话,默认的是全局命名空间
  • 上边结果第三个 10,显示的是 local's local 中的 a,使用 using first::a 将 first 中的 a 导入到当前作用域中
  • 上边结果第四个 40,显示的是 local 中的 a,此时退出 local's local 作用域,重新回到 local 作用域
  • 上边结果第五个 40,显示的是 local 中的 a,此时虽然使用 using namespace first 将全部变量导入 "global",但是由于 local 中存在 a,因此全局变量的 a 会被 局部变量的 a 所覆盖。

using namespace spacename 真的是将命名空间的内容引入到 global 中吗?我们可以简单的验证一下:

验证1:

#include <iostream>
#include <string>

using namespace std;

namespace first
{
int a = 10;
}

int a = 30;

int main()
{
    cout<<a<<endl;

    using namespace first;
    cout<<a<<endl;

    return 0;
}

上边的程序会提示:

error: reference to 'a' is ambiguous

 从上边报错结果来看,报错 ambiguous 是合理的。 

验证二:

#include <iostream>
#include <string>

using namespace std;

namespace first
{
int a = 10;
}

int a = 30;

int main()
{
    cout<<a<<endl;

    using namespace first;
    cout<<first::a<<endl;

    return 0;
}

结果为:

10

 这样的结果就很奇怪,因为如果真的是将 first 中的内容引入到 global 中,那么编译就不能通过,更不用说运行了。

可以知道应该不是引入到 global 当中。

验证三:

#include <iostream>
#include <string>

using namespace std;

namespace first
{
int a = 10;
}

int main()
{
    {
        using namespace first;
        cout<<a<<endl;
    }
    cout<<a<<endl;

    return 0;
}

结果会提示报错:

error: 'a' was not declared in this scope

 这样的结果显示应该是将 first 中的内容引入到了当前作用域当中。

验证四:

#include <iostream>
#include <string>

using namespace std;

namespace first
{
int a = 10;
}

int main()
{
    int a = 20;
    {
        using namespace first;
        cout<<a<<endl;
    }
    cout<<a<<endl;

    return 0;
}

结果为:

20
20

接上,如果真的是引入到了当前作用域当中,则在第一次输出就不应该被覆盖才对。

至于说为什么结果会这么奇怪,还不了解其中的奥妙,因此应该避免使用这种 namespace 的形式。最好还是用什么 using 什么,加上对应的绝对路径相对安全一点。

匿名 namespace

还可以不为命名空间建立名字:

#include <iostream>
#include <string>

using namespace std;

namespace
{
int a = 10;
}

int main()
{
    cout<<a<<endl;

    return 0;
}

结果为:

10

这种情况下,编译器会为该命名空间生成一个唯一名字,但是因为没有名字,我们在外部也无法使用。因此编译器会另外为这个匿名的命名空间生成 using 指令,类似于:

namespace __UNIQUE_NAME_
{
    int a;
}
using namespace __UNIQUE_NAME_;

从而直接将匿名空间的内容引入到了 ”global“ 当中。

多个匿名命名空间同时存在也是可以的:

#include <iostream>
#include <string>

using namespace std;

namespace
{
int a = 10;
}
namespace
{
int b = 20;
}
int main()
{
    cout<<a<<endl;
    cout<<b<<endl;
    return 0;
}

结果为:

10
20

同时匿名命名空间在不同的文件中经过编译的 __UNIQUE_NAME_ 都是不同的。在编译阶段避免了命名同名的问题,也就可以解决链接阶段不同编译文件的命名同名问题。

协同开发

在跨文件使用 namespace 和协同开发中,C++ 对 namespace 做出了规定:

  • 同名命名空间自动合并
  • 对于一个命名空间中的类,要同时包含声明和实现

命名空间别名

可以使用下边的形式为现有的命名空间添加新的别名,有点类似引用:

namespace new_name = current_name;
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值