C了个++:02 - C++的名称空间(层次)

梳理总结了有关C++中名称空间的知识

例程:名称空间示例(见最后)

名称:变量名、函数名、枚举、类、结构以及类和结构的成员名  等等

名称空间(层次):是为了更好地控制名称的作用域,使同一区域或不同区域中(同名)名称能相互独立不冲突

名称空间问题:来自不同类库的同名名称的冲突问题

注意区分:名称名 和 名称空间名、using声明 和 using编译指令




名称空间涉及概念:名称空间 vs 声明区域 vs 潜在作用域 vs 作用域(即对程序有效可见域,有效可用域)

  • (被嵌套的同名名称会使之前的名称隐藏不可见,被嵌套的区域对于被隐藏的那个名称是非有效作用域,但该区域是它的潜在作用域)
  • (每个声明区域声明的名称彼此独立,不过声明区域是有限的且没有层次对各个名称进行区分的,容易引发同名名称冲突问题或者导致覆盖隐藏某些名称
  • 名称空间是一种特殊的声明区域(C++特性),是一种独立于程序本身的声明区域的层次扩展,各个名称空间中声明的名称相互独立不冲突,并且可以在程序中的任何位置被使用(被调用),而且可以在同一个区域中使用(调用)来自不同名称空间(不同层次)的同名名称  且不冲突
  • 两个特殊的名称空间:global namespace(全局声明区域,该区域默认global名称空间,该区域声明的名称可以在主程序(main函数)中直接使用,不需要在主程序中再次进行using声明或using编译指令)    和     local namespace(代码块区域,其实就是函数中正常的声明区域,是不可定义的名称空间,可以通过using将该空间区域的名称链接到其它名称空间的名称上,并可以在该区域内控制该区域内同名名称之间的冲突问题和覆盖隐藏过程;另外,任何一个代码块都有一个local namespace相对应
  • 作用域(和local namespace相关)  <  潜在作用域  <  声明区域(其中全局声明区域默认为global namespace)  <  名称空间


名称空间的定义:namespace关键字(注意:比类和结构的声明最后少一个分号,因为它俩是类型,名称空间是域)

  • 名称空间中名称的声明和定义规则同全局声明区域名称的声明和定义规则相同,名称空间名需使用关键字namespace标识(类似声明类使用关键字class),但是global namespace和local namespace不使用关键字namespace
  • 名称空间可以外部全局定义,也可以在某名称空间中嵌套定义(可以用using编译指令把某整个名称空间嵌套添加到另一个名称空间中,类似类的继承过程),但名称空间不能位于代码块中定义
  • 默认情况下,在名称空间中声明的名称的链接性为外部的(除非它引用了常量);未命名的名称空间(只有关键字namespace没有名称空间名)的名称的链接性为所属文件的内部,它可以是内部静态变量的替代品
  • 名称空间是开放的,即可以在其它地方把名称添加到已有的名称空间中,即在不同位置对同一个名称空间的定义可以拼接组合,也可以利用using声明把其它名称空间的某个名称添加到另一个名称空间中(有点类似友元)
  • 在名称空间中声明的函数名的作用域为整个名称空间,因此该函数的定义和声明必须位于同一个名称空间中;注意,main()的声明和定义在global namespace中
  • namespace关键字可以给名称空间名创建新的别名,从而简化对嵌套的名称空间的使用,如namespace C=A::B;其中B是嵌套在名称空间A中的名称空间名
  • C++将标准类库或标准函数库放在名称空间std中,没有“.h”后缀的头文件都进行了这方面的过渡


名称空间中的名称的访问:使用名称空间名限定的名称(使用作用域解析运算符::)或使用using声明(后接名称名)或使用using编译指令(后接namespace和名称空间名)

  • 限定的名称 vs 未限定的名称(其中global namespace名称空间是“无名”的,直接使用::后接具体名称名)
  • 在某名称空间中的代码块中的local namespace可以直接使用该名称空间中的名称,不需要再次使用::或using,比如在main()中直接使用global namespace中的名称;注意,为什么main()中可以直接使用global namespace中的名称,而不用再使用::或using,是因为main()的声明和定义是属于global namespace的
  • using声明使特定名称空间的特定的名称可用,using编译指令使整个名称空间中的全部名称可用
  • using声明将特定的名称添加到当前语句所属的声明区域(local namespace)中,从而将local空间的名称与添加的那个名称空间的同名名称链接起来,这时候在这个区域中使用该名称时就可以省略其原来名称空间的限定
  • 在全局声明区域(或其它名称空间中)使用using声明调用某名称空间的名称时,会将该名称添加到全局名称空间global namespace(或其它名称空间)
  • 在任何local namespace中使用using编译指令时,会将引进的名称空间的所有同名名称全部链接到当前的local namespace中去,且局部可用,同样这时候在这个区域中使用该名称时就可以省略其原来名称空间的限定
  • 在全局声明区域(或其它名称空间中)使用using编译指令调用某整个名称空间时,会将该名称空间所有名称全部添加到全局名称空间global namespace(或其它名称空间)去,且全局可用
  • (“添加”和“链接”其实是同一个意思,这里是为了更形象的区分在global namespacelocal namespace中using的使用效果,因为local namespace是不可以定义的名称空间;注意,在local namespace中同一个名称的链接using声明只能使用一次,否则会产生二义性,但是可以使用带作用域解析的名称空间的限定名称,让多个同名的名称在local namespace中共存  且不冲突


使用using声明和using编译指令的主要区别:

  • using声明后接  作用域解析运算符解析的名称名,如using std::cout;  using编译指令后接  关键字namespace+名称空间名,如using namespace std;
  • using声明在local namespace中的名称的链接性是比较彻底的,再次声明新的同名名称会产生二义性;而using编译指令实际上是添加了一个局部名称空间,在local namespace中使用时的名称链接性并没有使用using声明的名称链接性彻底并不会覆盖全局名称空间中声明的的名称(有可能会产生二义性),甚至会被新声明的局部同名名称覆盖隐藏,换句话讲,函数中的using编译指令将名称空间的名称视为在函数之外声明的,函数体是名称空间的局部代码块,所以可以被新声明的局部同名名称覆盖隐藏,尽管此时被覆盖隐藏但依然可以在这个块中通过作用域解析运算符对其进行访问,实现同名名称的共存
  • 从名称空间的开放性和编译器的警告提示上来说,使用using声明或作用域解析运算符更安全;另外,声明为局部比全局更安全
  • 一般做法是,在每个函数(包括main()函数)的代码块开头使用using编译指令引入该函数需要的名称空间,让该名称空间在该函数中可用,但只能保证该名称空间的名称在该函数内部可以使用,不保证在其它之外的函数可用
  • 在使用using编译指令时,名称空间之间的嵌套性是可传递的,类似于类的继承过程,也可以看成是名称空间开放性的另一种表达:级联开放性
  • 在使用using声明函数名称时,并不描述函数的返回类型和特征标,只给出函数名名称,如果函数被重载了多种版本,则一个using声明将导入所有的版本
  • 另外,不要在头文件中使用using编译指令,因为这样不容易知道要让哪些名称可用,并且会受到包含头文件顺序的影响;using编译指令的使用应放在所有预处理器编译指令#include之后,应先添加  <头文件>  再添加  "头文件"




例程:名称空间示例

// 头文件namesp.h
#include<string>

namespace pers
{
	struct Person
	{
		std::string fname;		// 需要使用std名称空间的作用域解析,或者使用using
		std::string lname;
	};
	void getPerson(Person &);	// 结构最好使用引用做参数
	void showPerson(const Person &);
}

namespace debts
{
	using namespace pers;        // using编译指令,在debts中嵌套(添加)名称空间pers,从而在debts中可以使用pers中的名称
	struct Debt
	{
		Person name;
		double amount;
	};
	void getDebt(Debt &);
	void showDebt(const Debt &);
	double sumDebt(const Debt ar[],int n);
}


// 源代码文件namesp.cpp
#include<iostream>
#include"namesp.h"

namespace pers				// 名称空间的开放性,函数需要在同一个名称空间中声明和定义
{
	using std::cout;		// 不在头文件中使用using,这里使用的using声明添加的名称空间std的具体名称
	using std::cin;
	void getPerson(Person &rp)
	{
		cout << "Enter first name : ";
		cin >> rp.fname;
		cout << "Enter last name : ";
		cin >> rp.lname;
	}
	void showPerson(const Person &rp)
	{
		cout << rp.lname << ", " << rp.fname;
	}
}

namespace debts
{
	void getDebt(Debt &rd)
	{
		getPerson(rd.name);
		cout << "Enter debt : ";		// 在名称空间pers中已使用using声明添加了cout/cin,所以不用再次使用using声明,可传递性
		cin >> rd.amount;
	}
	void showDebt(const Debt &rd)
	{
		showPerson(rd.name);
		cout << " : $" << rd.amount << std::endl;		// 在名称空间pers中没有添加名称endl
	}
	double sumDebt(const Debt ar[],int n)
	{
		double sum = 0.0;
		for (int i = 0; i < n; i++)
			sum += ar[i].amount;
		return sum;
	}
}


// 源代码主文件namespace.cpp
#include<iostream>
#include"namesp.h"

void other();				  // 这里就是global namespace,它是“无名”的,不是未命名的,没有使用namespace关键字
void another();

int main()                    // main()也在global namespace中
{
	using debts::Debt;				// 主函数中除了全局名称空间之外,只使用了这两个名称,注意第二个名称是个函数名
	using debts::showDebt;			// 也可以不使用using声明,则在使用该名称时使用作用域解析运算符

	Debt golf = { {"Benny","Goatsniff"}, 120.0 };
	showDebt(golf);

	other();			        // 直接使用global namespace中的函数名称,不必使用using或::
	another();

	return 0;
}

void other()
{
	using std::cout;
	using std::endl;
	using namespace debts;					// 这个指令使得debts空间能用于该函数,并可以被该函数中的局部同名变量覆盖隐藏,不安全

	Person dg = {"Doodles","Glister"};		// using编译指令的可传递性
	showPerson(dg);
	cout << endl;

	Debt zippy[3];
	int i;
	for (i = 0; i < 3; i++)
		getDebt(zippy[i]);
	for (i = 0; i < 3; i++)
		showDebt(zippy[i]);
	cout << endl << "Total debt : $" << sumDebt(zippy,3) << endl << endl;

	return;
}

void another()
{
	using pers::Person;
	Person collector = {"Milo","Rightshift"};
	pers::showPerson(collector);
	std::cout << std::endl;

	return;
}





本文总结自《C++ primer plus》(第六版中文版)第九章:名称空间

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 第1章 基本类型 1.1 一个例程 1.2 类型与声明 1.3 基本类型 1.3.1 整数类型 1.3.2 字符类型 1.3.3 浮点类型 1.3.4 布尔类型 1.3.5 void类型 1.4 数值极限 1.5 标识符和关键词 1.5.1 标识符 1.5.2 关键词 1.6 练习 第2章 表达式和语句 2.1 作用域和存储分类 2.1.1 局部变量和全局变量 2.1.2 外部变量和寄存器变量 2.2 表达式 2.2.1 算术表达式 .2.2.2 关系表达式 2.2.3 逻辑表达式 2.2.4 位运算表达式 2.2.5 逗号表达式 2.3 语句 2.3.1 声明和初始化 2.3.2 赋值语句 2.3.3 复合赋值语句 2.3.4 增值减值语句 2.3.5 复合语句 2.3.6 条件语句 2.3.7 循环语句 2.4 斐波纳契数 2.5 练习 第3章派生类型 3.1 常量和宏 3.2 枚举类型 3.3 数组 3.4 结构 3.5 联合和比特域 3.6 指针 3.6.1 指针运算 3.6.2 多重指针 3.6.3 偏移量指针 3.6.4 常量指针 3.6.5 void指针和空指针 3.6.6 结构指针 3.6.7 字符指针 3.6.8 指针和数组 3.7 引用 3.8 函数 3.8.1 函数声明和定义 3.8.2 函数重载 3.8.3 参数传递 3.8.4 返回值 3.8.5 递归函数 3.8.6 内联函数 3.8.7 缺省参数 3.8.8 函数类型 3.8.9 静态局部变量 3.8.10 main函数 3.9 程序的运行空间 3.10 运算符概要及优先级 3.11 标准数学函数库 3.12 多项式求值 3.13 梯形公式和simpson公式 3.14 练习 第4章 命名空间和文件 4.1 命名空间 4.1.1 使用声明和指令 4.1.2 多重接口 4.1.3 命名空间别名 4.1.4 无名命名空间 4.1.5 名称查找 4.2 包含文件 4.2.1 包含标准库文件 4.2.2 用户自定义头文 4.2.3 条件包含指令 4.2.4 文件包含 4.3 源文件和连接 4.3.1 独立编译 4.3.2 外部连接和内部连接 4.3.3 与其他语言连接 4.4 一些有用的工具 4.4.1 给程序计时的方法 4.4.2 编译选项和调试器 4.4.3 创建库 4.4.4 makefile 4.5 字符串标准函数库 4.5.1 声明和初始化 4.5.2 操作 4.5.3 c语言字符串 4.5.4 输入输出 4.5.5 c字符串函数库 4.6 流标准函数库 4.6.1 整数格式化输出 4.6.2 浮点数格式化输出 4.6.3 输出宽度 4.6.4 文件的输入输出 4.6.5 字符的输入输出 4.6.6 字符串流 4.7 非线性方程的迭代解法 4.7.1 二分法 4.7.2 牛顿法 4.8 练习 第5章 类 5.1 类的声明与定义 5.2 拷贝构造函数和拷贝赋值 5.3 友元 5.4 静态成员 5.5 常量和可变成员 5.6 类的对象作为成员 5.7 类的数组 5.8 成员指针 5.9 常微分方程的数值解法 5.10 练习 第6章 运算符重载 6.1 复数 6.1.1 初始化 6.1.2 缺省拷贝构造和赋值 6.1.3 转换和混合模式运算 6.2 运算符函数 6.3 向量和矩阵 6.4 显式和隐式类型转换 6.5 效率和运算符重载 6.6 共扼梯度算法 6.7 练习 第7章 模板 7.1 类模板 7.1.1 成员和友元定义 7.1.2 模板实例化 7.1.3 模板参数 7.1.4 类型等价 7.1.5 用户定义的特化 7.1.6 特化顺序 7.2 函数模板 7.2.1 函数模板参数 7.2.2 函数模板重载 7.2.3 特化 7.2.4 类模板作为函数模板参数 7.2.5 成员函数模板 7.2.6 友元函数模板 7.3 模板源代码组织 7.4 标准复数库 7.5 标准ualarray库 7.5.1 ualarray类型 7.5.2 分段数组 7.5.3 广义分段数组 7.5.4 掩码数组和间接数组 7.6 数值算法标准函数库 7.6.1 累加 7.6.2 内积 7.6.3 部分和 7.6.4 临近差分 7.7 数值积分的高效技术 7.7.1 函数对象方法 7.7.2 函数指针作为模板参数 7.7.3 使用点积和模板表达式 7.7.4 采用点积和模板元程序 7.8 多项式插值 7.8.1 拉格朗日形式 7.8.2 牛顿形式 7.9 练习 第8章 类的继承 8.1 派生类 8.1.1 成员函数 8.1.2 构造函数和析构函数 8.1.3 拷贝 8.1.4 类层次结构 8.1.5 虚函数 8.1.6 虚析构函数 8.2 抽象类 8.3 访问控制 8.3.1 访问成员 8.3.2 基类访问 8.4 多重继承 8.4.1 去除二义性 8.4.2 重复基类 8.4.3 虚基类 8.4.4 多重继承中的访问控制 8.5 运行时的类型信息 8.5.1 动态投影机制 8.5.2 类型标识机制 8.5.3 运行时负担 8.6 用静态多态代替虚函数 8.7 练习 第9章 异常处理 9.1 抛出和捕获 9.2 派生异常 9.3 捕获异常 9.3.1 重抛出 9.3.2 捕获所有异常 9.3.3 处理函数的顺序 9.4 在函数中指定异常 9.5 标准异常 9.6 练习 第10章 容器和算法标准库 10 标准容器 10.1 向量 10.2 链表 10.1.3 映射与集合 10.1.4 栈和队列 10.2 标准算法 10.2.1 排序、复制和替换算法 10.2.2 搜索和遍历算法 10.2.3 集合、排列和堆算法 10.3 标准函数对象和适配器 10.3.1 算术函数对象 1o.3.2 关系函数对象 10.3.3 逻辑函数对象 10.3.4 标准适配器 10.4 练习 第11章 线性方程组求解法 11.1 矩阵存储格式 11.1.1 满矩阵 11.1.2 带状矩阵 11.1.3 稀疏矩阵 11.2 矩阵类层次 11.3 迭代算法 11.3.1 共轭梯度方法 11.3.2 广义最小残差法 11.3.3 预处理技术 11.4 高斯消元法 11.4.1 lu分解 11.4.2 高斯消元法 11.4.3 主元高斯消元法 11.5 求解偏微分方程的有限差分方法 11.6 练习 参考文献

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值