跳至主要內容

命名空间

张威大约 9 分钟c/c++c++基础

命名空间

image-20240101150131801
image-20240101150131801

为什么要使用命名空间?

一个大型的工程往往是由若干个人独立完成的,不同的人分别完成不同的部分,最后再组合成一个完整的程序。由于各个头文件是由不同的人设计的,有可能在不同的头文件中用了相同的名字来命名所定义的类或函数,这样在程序中就会出现名字冲突。不仅如此,有可能我们自己定义的名字会与C++库中的名字发生冲突。

名字冲突就是在同一个作用域中有两个或多个同名的实体,为了解决 ,C++中引入了命名空间,所谓命名空间就是一个可以由用户自己定义的作用域,在不同的作用域中可以定义相同名字的变量,互不干扰,系统能够区分它们。

什么是命名空间

命名空间又称为名字空间,是程序员命名的,程序员根据需要指定一些有名字的空间域,把一些全局实体分别存放到各个命名空间中,从而与其他全局实体分隔开。通俗的说,每个名字空间都是一个名字空间域,存放在名字空间域中的全局实体只在本空间域内有效。名字空间对全局实体加以域的限制,从而合理的解决命名冲突。

基本格式

C++中定义命名空间的基本格式如下:

namespace wd
{
int val1 = 0;	//不需要table,直接正常顶格写
char val2;
}// end of namespace wd	//不需要写;

不需要写;,写了相当于执行一句空语句

存放的类型

在声明一个命名空间时,大括号内不仅可以存放变量,还可以存放以下类型:

  • 常量
  • 变量
  • 函数,可以是定义或声明
  • 结构体
  • 模板
  • 命名空间,可以嵌套定义
  namespace wd
  {
  int number = 0;
  struct Foo
  {
      char ch;
      int val;
  };
  void display();
  }// end of namespace wd

可见域

定义在名称空间中的变量或者函数都称为实体,名称空间中的实体作用域是全局的, 并不意味着其可见域是全局的。

  • 如果不使用作用域限定符和using机制,抛开名称空间嵌套和内部屏蔽的情况,实体的可见域是从实体创建到该名称空间结束。在名称空间外,该实体是不可见的。

命名空间的使用方式

命名空间一共有三种使用方式,分别是using编译指令作用域限定符using声明机制

using编译指令

我们接触的第一个C++程序基本上都是这样的,其中std代表的是标准命名空间。

#include <iostream>
using namespace std;	//using编译指令
int main(int argc, char *argv[])
{
    cout << "hell,world" << endl;   
    return 0;
}

其中第二行就使用了using编译指令。如果一个名称空间中有多个实体,使用using编译指令,就会把该空间中的所有实体一次性引入到程序之中;对于初学者来说,如果对一个命名空间中的实体并不熟悉时,直接使用这种方式,有可能还是会造成名字冲突的问题,而且出现错误之后,还不好查找错误的原因,比如下面的程序就会报错,当然该错误是人为造成的。

#include <iostream>
using namespace std;
double cout()	//error 代码二义性
{
    return 1.1;
}

int main(void) 
{
    cout();	//error 代码二义性
    return 0;
}

作用域限定符

第二种方式就是直接使用作用域限定符::啦。每次要使用某个名称空间中的实体时,都直接加上,例如:

namespace wd
{
int number = 10;
void display()
{
    //cout,endl都是std空间中的实体,所以都加上'std::'命名空间
    std::cout << "wd::display()" << std::endl;
}
}//end of namespace wd

int main(void) 
{
    std::cout << "wd::number = " << wd::number << endl;
    wd::display();
}

这种方式会显得比较冗余,所以还可以采用第三种使用方式。

using声明机制👍

using声明机制的作用域从using语句开始,到using所在的作用域结束

#include <iostream>
using std::cout;	//using声明机制
using std::endl;
namespace wd
{
int number = 10;
void display()
{
    cout << "wd::display()" << endl;
}
}//end of namespace wd

using wd::number;
using wd::display;
int main(void) 
{
    cout << "wd::number = " << number << endl;

创建全局变量全局函数的时候:

  • 将自己的全局变量和全局函数放到

匿名命名空间

命名空间还可以不定义名字,不定义名字的命名空间称为匿名命名空间。由于,该空间中的实体,,它只能在本文件的作用域内有效,它的作用域是从匿名命名空间声明开始到本文件结束。在本文件使用无名命名空间成员时不必用命名空间限定。其实匿名命名空间和static是同样的道理,都是只在本文件内有效,无法被其它文件引用。

namespace {
int val1 = 10;
void func();
}//end of anonymous namespace

在匿名空间中创建的全局变量,具有全局生存期,却只能被本文件空间内的函数等访问,

匿名命名空间的本质是什么?就是将命名空间的名字符号给去掉,让其他文件找不到。

C++ 新的标准中提倡使用匿名命名空间,而不推荐使用static,因为static用在不同的地方,涵义不同,容易造成混淆。另外,static不能修饰class

命名空间的嵌套及覆盖

int number = 1;
namespace wd
{
int number = 10;

namespace wh	//嵌套
{
int number = 100;
void display()
{
    cout << "wd::wh::display()" << endl;
}
}//end of namespace wh
    
void display(int number)
{
    cout << "形参number = " << number << endl;	//20
    cout << "wd命名空间中的number = " << wd::number << endl;	//10
    cout << "wh命名空间中的number = " << wd::wh::number << endl;	//100
    cout << "全局number = " << ::number << endl;	//1	
}
    
}//end of namespace wd

int main(void)
{
    using wd::display;
    display(20);
    return 0;
}

对命名空间的思考和总结

下面引用当前流行的名称空间使用指导原则:

  • 提倡已命名的,而不是直接定义外部全局变量或者静态全局变量。
  • 如果开发了一个或者,提倡将其放在一个名称空间中。
  • 对于using 声明首先将其作用域设置为局部而不是全局(临近使用的地方声明)
  • 不要在头文件中使用using编译指令,这样,使得可用名称变得模糊,容易出现二义性,
  • 包含头文件的顺序可能会影响程序的行为,如果非要使用using编译指令,建议放在所有#include预编译指令

课内代码

#include <iostream>

using namespace std;

#if 0	//注释方法
void HWprint()
{

}

void HFCPPProjectPrint()
{

}
#endif

namespace wd
{
//命名空间中可以定义变量、函数、结构体、命名空间,统称为实体
int number = 10;

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

}//end of namespace wd
/* ; */

int main(int argc, char **argv)
{
    cout << "number = " << wd::number << endl;//作用域限定符
    wd::print();
    return 0;
}
#include <iostream>

using namespace std;//1、using编译指令,可以将命名空间中的实体全部引出来

//自定义的实体与命名空间中的实体冲突
int cout()
{
    return 10;
}

int main(int argc, char **argv)
{
    cout();
    return 0;
}
#include <iostream>

/* using namespace std;//1、using编译指令,可以将命名空间中的实体全部引出来 */

namespace wd
{
//命名空间中可以定义变量、函数、结构体、命名空间,统称为实体
int number = 10;

void print()
{
    std::cout << "void print()" << std::endl;
}

}//end of namespace

//2、命名空间+作用域限定符的形式,即使自定义的实体命名空间中的
//实体冲突,一样没有影响
void cout(int x, int y)
{
    std::cout << "x = " << x << ", y = " << y << std::endl;
}

int main(int argc, char **argv)
{
    std::cout << "number = " << wd::number << std::endl;//作用域限定符
    wd::print();
    cout(3, 5);
    return 0;
}
/*using 声明机制*/

#include <iostream>

using std::cout;//3、using声明机制,一次只引出一个实体
using std::endl;//推荐使用这种

namespace wd
{
//命名空间中可以定义变量、函数、结构体、命名空间,统称为实体
int number = 10;

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

}//end of namespace

int main(int argc, char **argv)
{
    cout << "number = " << wd::number << endl;//作用域限定符
    wd::print();
    return 0;
}
/*命名空间是可以进行扩展的,结构体是不能进行扩展的*/

#include <iostream>

using std::cout;
using std::endl;

//标准命名空间可以进行扩展
//有可能会与std中的实体冲突,一般不建议进行扩展(标准命名空间中的实体一般都是小写)
namespace  std
{
struct MyStruct
{
    int ia = 0;
};
}//end of namespace std

#if 0
//结构体是不能进行扩展的
struct A
{
int ia;
};

struct A
{
int ib;

};
#endif
//命名空间是可以进行扩展的
//带命名空间的函数声明
namespace  wd
{
void print();
}

namespace hb
{
int number = 20;

void show()
{
    cout << "void show()" << endl;
}

void display()
{
    cout << "void display()" << endl;
    wd::print();
}

}//end of namespace hb

namespace wd
{
int number = 10;

void print()
{
    cout << "void print()" << endl;
    hb::show();
}

}//end of namespace

int main(int argc, char **argv)
{
    cout << "number = " << wd::number << endl;//作用域限定符
    wd::print();
    return 0;
}
#include <stdio.h>
#include <iostream>

using std::cout;
using std::endl;

int number = 1;

namespace wd
{
int number = 10;

void print(int number)
{
    cout << "形参number = " << number << endl;
    cout << "命名空间中number = " << wd::number << endl;
    cout << "全局number = " << ::number << endl;//匿名命名空间
    printf("helloworld\n");	//没有重名,可以直接写,不需要加匿名空间作用域限定符
    ::printf("helloworld\n");
}

//命名空间是可以嵌套的
namespace wh
{
int number = 200;
}

}//end of namespace

int main(int argc, char **argv)
{
    wd::print(20);
    cout << "wh::number = " << wd::wh::number << endl;
    return 0;
}