C++面向对象程序设计 - 命名空间

命名空间是ANSI C++引入的可以由用户命名的作用域,用来处理程序中常见的同名冲突。

在C语言中定义了三个层次的作用域,即文件(编译单元)、函数和复合语句。C++又引入了类作用域,类是出现在文件内的。在不同的作用域中可以定义相同名字的变量,互不干扰,系统能够区别它们。

例如两个不同的头文件中,命名了的类名相同,同时引入进来会报错【[Error] redefinition of 'class Student'】- 这个错误表明类 Student 被重复定义了。示例如下:

1、s1.h头文件代码:

cpp 复制代码
#include <string>
using namespace std;
// 定义Student学生类
class Student{
	private:
		int num;
		string name;
		int age;
	public:
		Student(int num, string name, int age): num(num), name(name), age(age){}
};

2、s2.h头文件代码:

cpp 复制代码
#include <string>
using namespace std;
// 定义Student学员类
class Student{
	private:
		int num;
		string name;
		int age;
		float wage;		//工资
	public:
		Student(int num, string name, int age, float wage): num(num), name(name), age(age), wage(wage){}
};

3、主文件代码:

cpp 复制代码
#include "s1.h"
#include "s2.h"
using namespace std;

int main(){
	return 0;
}

以上代码在编译的时候就会出现名字冲突,为了解决这个问题,ANSI C++增加了命名空间(namespace)。所谓命名空间,实际上一个由程序设计者命名的内存区域,可以根据需要指定一些有名字的空间域,把一些全局实体分别放到各个命名空间中,从而与其他全局实体分隔开来。

如下代码:

cpp 复制代码
namespace ns1{
    int a;
    double b;
}

一、命名空间

在声明一个命名空间时,花括号内不仅可以包括变量,而且还可以包括以下类型:

  • 变量(可以有初始化)
  • 常量
  • 函数(可以是定义或声明)
  • 结构体
  • 模板
  • 命名空间(在一个命名空间中又定义一个命名空间,即嵌套的命名空间)

示例如下:

cpp 复制代码
#include <iostream>
using namespace std;
namespace ns1{
	const double PI = 3.1415926;
	int age = 30;
	double area(){
		return 2 * 20 * PI;
	}
	namespace ns2{
		float wage = 1500.0f;
	}
}

int main(){
	cout <<ns1::PI <<endl;
	cout <<ns1::age <<endl;
	cout <<ns1::area() <<endl;
	cout <<ns1::ns2::wage <<endl;
	return 0;
}

运行结果如下图:

二、使用命名空间解决同名冲突

有了以上的基础后,就可以利用命名空间来解决前端Student类的名字冲突问题了。代码如下:

1、s1.h文件

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

namespace ns1{
	// 定义Student学生类
	class Student{
		private:
			int num;
			string name;
			int age;
		public:
			Student(int num, string name, int age): num(num), name(name), age(age){}
			void print(){
				cout <<"num:" <<num <<", name:" <<name <<", age:" <<age <<endl;
			}
	};
}

2、s2.h文件

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

namespace ns2{
	// 定义Student学员类
	class Student{
		private:
			int num;
			string name;
			int age;
			float wage;		//工资
		public:
			Student(int num, string name, int age, float wage): num(num), name(name), age(age), wage(wage){}
			void print(){
				cout <<"num:" <<num <<", name:" <<name <<", age:" <<age <<", wage:" <<wage <<endl;
			}
	};
}

3、main主文件

cpp 复制代码
#include "s1.h"
#include "s2.h"
using namespace std;

int main(){
	ns1::Student s1(100, "Tom", 20);
	ns2::Student s2(1001, "John", 30, 1500.0f);
	// 打印结果信息
	s1.print();
	s2.print();
	return 0;
}

运行结果如下图:

三、使用命名空间成员的方法

在引用命名空间成员时,要用命名空间名和作用域分辨符对命名空间成员进行限定,以区别不同的命名空间中的同名标识符,如:

命名空间名::命名空间成员名

这种方法是有效的,并保证所引用的实体有惟一的名字。但是如果命名空间名字比较长,尤其在有命名空间嵌套的情况下,为引用一个实体,需要写很长的名字。为此,C++提供了一些机制,能简化使用命名空间成员的手续。

(1)使用命名空间别名

可以为命名空间起一个别名(namespace alias),用来代替较长的命名空间名。例如下:

cpp 复制代码
#include <iostream>
using namespace std;
namespace ns1{
	const double PI = 3.1415926;
	int age = 30;
	double area(){
		return 2 * 20 * PI;
	}
	namespace ns2{
		float wage = 1500.0f;
	}
}

int main(){
	cout <<ns1::PI <<endl;
	cout <<ns1::age <<endl;
	cout <<ns1::area() <<endl;
	// 用别名n2代替ns1::ns2
	namespace n2 = ns1::ns2;
	cout <<n2::wage <<endl;
	return 0;
}

运行结果还是一样的,如下图:

(2)使用using命名空间成员

using后面的命名空间成员名必须是由命名空间限定的名字。例如:

cpp 复制代码
#include <iostream>
using namespace std;
namespace ns1{
	const double PI = 3.1415926;
	int age = 30;
	double area(){
		return 2 * 20 * PI;
	}
}

int main(){
	// 声明其后出现的变量或函数都是属于命名空间ns1中的
	using ns1::PI;
	using ns1::age;
	using ns1::area;
	// 此处引用相当于ns1::PI, ns1::age, ns1::area()
	cout <<PI <<endl;
	cout <<age <<endl;
	cout <<area() <<endl;
	return 0;
}

当然,前面Student示例如,如使用using则会产生二义性,编译出错,代码如下:

cpp 复制代码
#include <iostream>
#include "s1.h"
#include "s2.h"
using namespace std;

int main(){
	using ns1::Student;
	Student s1(100, "Tom", 20);
	// 错误,会出现二义性
	using ns2::Student;
	Student s2(1001, "John", 30, 1500.0f);
	// 打印结果信息
	s1.print();
	s2.print();
	return 0;
}

所以要注意,在同一作用域中用using声明的不同命名空间的成员中不能有同名的成员。

(3)使用using namespace 命名空间名

在上面介绍的using命名空间成员,一次只能声明一个命名空间成员,如果在一个命名空间中定义了多个实体,就需要使用多次using命名空间成员名。C++为此提供了using namespace 语句为实现只要用一个语句就能一次声明命名空间中全部成员。示例如下:

cpp 复制代码
#include <iostream>
#include "s1.h"
using namespace std;
using namespace ns1;
int main(){
	Student s1(100, "Tom", 20);
	// 打印结果信息
	s1.print();
	return 0;
}

运行结果如下图:

当然,此用法同"(2)使用using命名空间成员"一样,使用多个命名空间时,当多个命名空间中出现同名时,容易出错。如下代码:

cpp 复制代码
#include <iostream>
#include "s1.h"
#include "s2.h"
using namespace std;
using namespace ns1;
using namespace ns2;

int main(){
	Student s1(100, "Tom", 20);
	//错误,会出现二义性
	Student s2(1001, "John", 30, 1500.0f);
	// 打印结果信息
	s1.print();
	s2.print();
	return 0;
}

此时编译时会报错【[Error] reference to 'Student' is ambiguous】- 因同一作用域中引入两个命名空间ns1和ns2,其中有同名的类,此时无法判定是哪个命名空间的Student,所以会出现二义性,编译出错。

四、无名的命名空间

C++还允许使用没有名字的命名空间,如在文件中声明了以下无名命名空间,代码如下:

cpp 复制代码
#include <iostream>
using namespace std;
// 定义无名命名空间
namespace{
	void test(){
		cout <<"Test...";
	}
}

int main(){
	test();
	return 0;
}

运行结果如下图:

由于命名空间没有名字,在其他文件中显然无法引用,它只在本文件的作用域内有效。其作用是更该函数的作用域限定于本文件。C++保留了用static声明函数的用法,同时提供了用无命名空间来实现这一功能,使用无命名空间成员的方法将会取代以前习惯用的对全局变量的静态声明。

五、标准命名空间std

为了解决C++标准库中的标识符与程序中的全局标识符之间以及不同库中的标识符之间的同名冲突,应该将不同库的标识符在不同的命名空间中定义(或声明)。标准C++库的所有标识符都在一个名为std的命名空间中定义的,或者说标准头文件中函数、类、对象和类模板是在命名空间std中定义的。std是standard(标准)的缩写,表示这是存放标准库的有关内容的命名空间。

在程序中用到C++标准库时,需要使用std作为限定,代码如下:

cpp 复制代码
std::cout <<"OK" <<endl;

C++介绍到这里,想必大家已了解命名空间的使用。这就是大家有时看到文件中未引用命名空间std时,标准库中的成员侧需要使用std引用,代码如下:

cpp 复制代码
std::cout <<"Hello World" <<std::endl;

当引用了命名空间std时,又可以省略掉std,代码如下:

cpp 复制代码
using namespace std;

cout <<"Hello World" <<endl;

所以在标准的C++编程中,应对命名空间std的成员进行声明或限定。

相关推荐
stm 学习ing5 分钟前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc1 小时前
《Python基础》之字符串格式化输出
开发语言·python
mqiqe2 小时前
Python MySQL通过Binlog 获取变更记录 恢复数据
开发语言·python·mysql
AttackingLin2 小时前
2024强网杯--babyheap house of apple2解法
linux·开发语言·python
Ysjt | 深2 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
ephemerals__2 小时前
【c++丨STL】list模拟实现(附源码)
开发语言·c++·list
码农飞飞2 小时前
深入理解Rust的模式匹配
开发语言·后端·rust·模式匹配·解构·结构体和枚举
一个小坑货2 小时前
Rust 的简介
开发语言·后端·rust