C++名称空间

名称空间

名称空间可以是全局的,也可以位于另一个名称空间中,但不能位于代码块中。因此,在默认情况下,在名称空间中声明的名称的链接性为外部的(除非它引用了常量)

名称空间是开放的,你可以在名称空间中追加内容

namespace geo{
 double x;
 double y;
 void func(); //函数原型
} 
namespace geo{
 double z;  // z 也会放入名称空间
 void func(){
 	std::cout << "func \n" ;
 }				// 你可以在这里实现函数原型
}

注意不能在名称空间完成赋值操作,如果想初始化则必须写成初始化的形式

namespace geo{
	double x =100; //可以初始化
	x = 1.5; // 不能赋值,这条语句将会报错
}

你也可以将函数的声明和定义写在一起,就不用提供原型

namespace geo{
double add(double x,double y){
	return x+y;
}
}

也可以分文件进行编写,将原型写到头文件里

// head.h
#pragma once
namespace geo{
    double add(double,double);                                                           
}
// main.cpp
#include <iostream>
#include "head.h"
double geo::add(double x,double y){
    return x+y;
}
int main()
{
    std::cout << geo::add(1.0,3.0) << '\n';                                              
    return 0;
}

你可以看到和类的定义写法差不多,可以分文件进行编写

在局部变量隐藏了全局变量的时候,如何调用全局变量?

#include <iostream>
int x = 100;
int main()
{
    using namespace std;
    int x =10;
    cout << "local var x = " <<  x << " &x = " << &x <<endl;
    cout << "global var x = " <<  ::x << " &x = " << &::x <<endl;                        
    return 0;
}

在这里插入图片描述

默认全局变量都会处于全局名称空间,你可以通过::操作符来获取全局变量,如果你自己定义了一个全局名称空间例如geo你也可以通过 ::geo::x来访问geo里面的变量,但是这没什么意义,你本可以直接用geo::x,不过从这里你可以看出geo是属于全局名称空间的一部分,可能你会想起前面提到的,名称空间定义是全局的或者位于另一个名称空间里。

你可以利用namespace给名称空间取别名

namespace geox = geo;  

这句话会让geox成为geo的别名,这种用法在存在名称空间嵌套的时候比较要用例如

  namespace geo{
      namespace line{
          int x;
      }
  }
  namespace geox = geo::line; //这会让geox变为geo::line的别名

using关键字

  1. using声明,你可以使用using namespace::var来进行使用var,例如经常使用的using std::coutstd为标准名称空间
  2. using编译指令,这会将名称空间的所有名称都可用,例如using namespace std,这样你就能尽情使用标准名称空间的内容

这两者有什么区别?
using声明就是真实的声明,你使用了之后就不能声明相同的名字的变量,例如如下的代码不能通过编译

  #include <iostream>
  namespace geo{
      int x;
  }
  int main()
  {
      using geo::x;
	  double x;  // 出现声明冲突                                                                         
      return 0;
  }

using编译指令是允许你定义重名的变量,这会隐藏掉名称空间里面的变量

#include <iostream>
namespace geo{
    int x;
}
int main()
{
    using namespace geo;
    double x=100;
    std::cout << "x = "<< x << " &x = " << &x <<std::endl;
    std::cout << "geo::x = "<< geo::x << " &x = " << &geo::x <<std::endl;  // 覆盖后还想调用则使用geo::             
    return 0;
}

在这里插入图片描述
如果有两个名称空间有同名变量会发生什么?

  #include <iostream>
  namespace geo{
      int x=0;
  }
  namespace geox{
      int x =1;
  }
  int main()
  {
      using namespace geo;
      std::cout << "x = "<< x << " &x = " << &x <<std::endl;
      using namespace geox;
      std::cout << "x = "<< x << " &x = " << &x <<std::endl;
      return 0;                                                                          
  }

答案是这无法通过编译,会产生模糊定义,编译器不知道选择哪个x(即使你后用的using编译指令也无法覆盖前面的定义)
如果混用using声明会发生什么?

#include <iostream>
namespace geo{
    int x=0;
}
namespace geox{
    int x =1;
}
int main()
{
    using geo::x;
    std::cout << "x = "<< x << " &x = " << &x <<std::endl;                               
    using namespace geox;
    std::cout << "x = "<< x << " &x = " << &x <<std::endl;
    return 0;
}

答案是两个x均是0,因为using声明的行为就是声明,他会覆盖掉using编译的结果,即使编译命令在后面
在这里插入图片描述
如果都使用using声明可以吗?

  #include <iostream>
  namespace geo{
      int x=0;
  }
  namespace geox{
      int x =1;
  }
  int main()
  {
      using geo::x;
      std::cout << "x = "<< x << " &x = " << &x <<std::endl;
      using geox::x;
      std::cout << "x = "<< x << " &x = " << &x <<std::endl;                             
      return 0;    
  }   

想必你已经想到了结果,因为using声明就是声明!这会产生重定义,而不能通过编译

其他特性

可以在名称空间中使用using关键字

namespace geo{
    int y = 1;
    int x =100;
    namespace line{
        int x=0;
    }                                                                                    
}
namespace geox{
    using namespace geo;
    using geo::line::x;
    int y =100;
}

我这里面其实有很多问题,先要说明的是using编译指令具有传递性
使用

using namespace geox;

相当于

using namespace geox;
using namespace geo;

注意没有 using geo::line::x因为这是声明而不是编译指令
所以

using namespace geox;
std::cout << x ; //模糊的定义

因为geox中有一个xgeo中也有一个x这和之前提到的两个名称空间具有相同的名称是一样的

接下来分析一下geox::x的值是多少?
前面说过声明会覆盖using编译指令,所以geox中的x实际上是geo::line::x的别名,所以值为0

同样

using namespace geox;
std::cout << y ; //模糊的定义

这里就很好理解了,geox包含了自己定义的ygeo也一样和前面的情况完全一致。

接下来注意名称空间中使用using namespace geo并不意味着geox包含了geo,而是相当于把,geo里面的名称都导入了geox,这可以被覆盖,我们前面诸多例子说明了这个现象,所以你其实不能用geox::geo,但是你可以用geox::line这与geo::line完全相同,可以理解为别名

最后看一种更为复杂的情况

  #include <iostream>
  namespace geo{
      namespace line{
          int x=0;
      }
  }
  namespace geox{
      using namespace geo;
      namespace line{
      int y=10;
      }
  }
  int main()
  {
      using namespace geox;
      using namespace std;
      cout << line::x; // 模糊的定义                                                                   
      return 0;
  }     

这会编译出错,因为无法确定使用哪一个line,因为即使你使用using name geogeox包含了geo的内容,可能你以为这可以利用namespace的开放原则,可以将int y=10;添加到geo中,但实际上不行,编译器只会看到geox自己定义的line,由于前面提到的using 传递原则所以会出现模糊的定义,因为有两个line都可见,这与前面提到的两个名称空间包含相同的名称类似,并且你没有办法通过geox访问到geo中的内容,这一点很有意思

匿名namespace
实现文件作用域的静态变量替代方案,使得变量具有内部链接,别的文件不可见

namespace {
	int x; // x是静态变量 ,具有内部链接性
}

这个效果于static int x;
但是匿名namespace的好处更多

  1. 可以分组对一批内容设定内部链接性
  2. 支持模板,类/结构 ,static无法修饰这些
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值