C++命名空间学习笔记
1 模块化和界面
任何实际程序都是有一些部分组成的。通过将程序进行模块化可以使我们的程序更加清晰,有助于多人合作和维护。
将一个程序进行模块化以后,当其中一个模块调用另一个模块时,它不需要知道其具体实现,只需要调用它提供的接口即可。因此一个模块应该是由两个部分组成:具体实现和提供给外部的接口。
2 命名空间
2.1命名空间的作用
命名空间相当于一个容器,它里面包含了逻辑结构上互相关联的一组类、模板、函数等。也就是说如果某些“对象”在逻辑上有关系,我们就可以将它们放到一个命名空间里用以和外界进行区分。命名空间一个显著的特点是命名空间内的变量(类等)名可以和命名空间以外的重名。这可以用来将不同人写的代码进行整合。
命名空间的使用格式如下:
namespace A
{
void Fun1(){...};
void Fun2(){...};
}
上面的组织形式我们将函数的具体实现和声明放到了一起,有时候我们并不想看到函数的具体实现,只希望能一眼看到的全部都是函数的接口界面。我们可以采用如下的方式将函数的界面和具体实现分开。
namespace A
{
void Fun1();
void FUn1();
}
void A::Fun1(){/*...*/}
void A::Fun2(){/*...*/}
- 如果一个函数的定义没有在其对应的命名空间里,必须要使用作用域解析符::来指定函数的命名空间。
- 不可以在命名空间以外定义一个命名空间中不存在的新成员。例如:
void A:Fun3(); //错误,A里并没有Fun3()
- 一个良好的程序应该将程序中的所有实体(变量,类,函数)都放到某个命名空间里。当然除了main()函数之外。
2.2命名空间作用域规则
命名空间是一个作用域,因此它具有普通作用域的规则。
- 如果一个变量之前在该命名空间或其外围作用域声明过,则它可以直接使用。
- 使用来自另外一个命名空间的变量,需要加上作用域解析符。
- 如果频繁的使用另外一个命名空间的实体,可以使用using将其引入,之后再使用时就不用加作用域解析符了。
double A::Fun()
{
using B::Fun1; //使用B命名空间的函数Fun1;
using C::Var1; //使用C命名空间的变量Var1;
void Fun1(Var1); //B::Fun1
}
- 在函数中使用using则外部变量的作用域限于函数,如果在命名空间中使用using,则作用范围扩大到整个命名空间。如果使用到一个命名空间中的多个实体,可以直接用using namespace Name将命名空间中所有的实体都引入,但是让一个命名空间的所有实体都可以被另一个命名空间访问并不是一个安全的做法,应尽量避免。
namespace A
{
using namespace B; //使用命名空间B中所有实体
}
- 如果使用using引入的实体之间或者和本命名空间中的实体有重名的。那么加上作用域解析符能帮助我们更好的对它们进行区分。
2.3多重界面
有时候我们同一个命名空间在面向不同的用户时,可能需要提供不同的界面。比如我们有一个命名空间里面定义了关于串口的一些实体。我们给一个开发中的程序提供的接口可能包括:打开串口,设置波特率,设置校验位等。但是面向一个最终用户时,我们可能只需要给他提供一个打开串口接口就够了。这便是使用多重界面的意义。
- 实现多重界面的方法有很多,首先可能想到的是使用不同的命名空间。
namespace A
{
void Fun1();
void Fun2();
void Fun3();
}
namespace A_Interface1
{
using A::Fun1;
}
- 上面界面实现的过程中,A_Interface1和A有着非常强的关联,修改A中的Fun1会使A_Interface1中的Fun1也修改。有时候我们可能不需要这么强的关联性,让A_Interface1中的函数有着一定的可控性,可以使用下面界面实现的方式。
namespace A
{
void Fun1();
void Fun2();
void Fun3();
}
namespace A_Interface1
{
void Fun1(){A::Fun1();}
}
这里为了书写方便将A_Interface1中Fun1的声明和定义放到一起了。上面这种界面实现形式保证了A_Interface1中的Fun1有一定的自主性,当A中的Fun1改变时,我们可以在A_Interface1中的Fun1进行进一步的调整,或者干脆不用A中的Fun1重新实现Fun1.这种界面处理方式去除了一定的耦合性,基本已经能够满足大部分需求了。
3.当然我们也可以不重新定义一个命名空间直接使用原来命名空间的名字,但是这样容易误导程序设计者以外的人(让看到这个接口的人以外该命名空间只有声明的这些实体)。
namespace A
{
void Fun1();
void Fun2();
void Fun3();
}
//使用接口的文件包含下面这个声明或其所在的文件
namespace A
{
void Fun1();
}
2.4无名命名空间
我们知道不同命名空间的变量名可以重复,这有助于第三方将两个不同人写的代码进行整合。有时候我们并不想我们的某些代码被其他人进行整合,但是也想利用命名空间的优势——可以让变量名重复。这时候使用无名命名空间就很有价值了:第一没有名字,其他地方无法引用进去;第二因为是命名空间它里面的变量可以和其他命名空间中变量的名字重复。
无名命名空间可以在本编译单元(所在文件)处调用,没有这一规则就永远都用不到了。需要注意的是,不同编译单元中的无名命名空间不同。
2.5编译器的名字查找
- 一个函数Fun1如果有一个参数为T类型,那么一般情况下类型T和函数Fun1都在同一个命名空间里。编译器在调用使用T为参数的函数时,也会隐式的从T(和其基类)所在命名空间寻找被调用函数。编译器这种隐式的查找过程可以使程序员省掉许多显式的限定符或using指令。而且这个查找机制在对象的运算符运算和模板参数中特别有用。
namespace A
{
class TypeA{...};
void Fun1(TypeA a){...};
}
void Fun(A::TypeA a)
{
Fun1(a); //可以,会在TypeA所在命名空间A找到Fun1
Fun1(2); //不行
}
当然,命名空间本身必须在作用域里,函数也必须在寻找和使用之前声明。
- 如果调用的函数Fun1中有两个参数,且这两个参数所在命名空间都找到了Fun1函数。那么就会调用重载解析规则。
namespace A
{
class TypeA{...};
bool operator==(const A&,std::string&);
}
void Fun(A::TypeA a,std::string str)
{
if(a == str)
{...}
}
在上例中表达式a==str
中有两种数据类型,且这两种数据类型所在的命名空间A,std都定义了operator(string的运算符的定义在std中),于是重载解析规则就会被调用。由于std::operator不以TypeA为参数,所有最后编译器会调用A::operator.
3. 当一个类的成员调用一个函数时,编译器查找函数时偏向于在同一类和其基类中查找函数,而不是在被调用函数其他参数类型所在的命名空间查找。但这一规则对运算符(如+,-)查找并不适用。
2.6命名空间的别名
我们在给命名空间取名字的时候,如果太短(比如上面的A)很可能出现冲突。起太长又太麻烦。这时候我们将长名字的命名空间在合适的地方取个别名可能会更好些。格式如下:
namespace A = LongNameNamespaceA;
这种替换的方式和C语言中的宏定义非常相似,因此别名另外一个特别用于的地方就是使代码对命名空间的依赖降低。比如我们有一个大型程序依赖于一个命名空间LibA,现在需要版本升级将所有引用LibA中的代码替换为LibA_Plus.如果使用起别名的方式,我们不用再代码中逐个查找将LibA::
替换为LibA_Plus
.而是简单的在命名空间的别名定义处稍作修改即可。
namespace A = LibA;
//替换为
namespace A = LibA_Plus;
但也需要注意过多的使用命名空间别名也容易造成混乱。
2.7命名空间的组合
前面我们说了命名空间里可以包含其他的命名空间,下面给出命名空间组合时的一些具体细节。
- 再次强调在命名空间里没有声明的实体,不能在别处定义。即使这个实体在其包含的命名空间里声明过了。
namespace A
{
using namespace B;
}
namespace B
{
fill(char C);
}
A::fill(char C) //错误 A中并没有声明fill函数
{...}
- 引入一个命名空间的实体时可以引入它的所有重载。
namespace B
{
class String{...};
String operator+(const String&,const String&);
String operator+(const String&,const char*);
}
namespace A
{
using B::String;
using B::operator+; //使用B中所定义的全部(两个)+运算
}
- 显示的使用using关键字可以改变重载的顺序。
namespace LibA
{
class String{...};
template<class T>class Vector{...};
//其他实体
}
namespace LibB
{
class String{...};
template<class T> class Vector{...};
//其他实体
}
namespace LibC
{
using namespace LibA;
using namespace LibB;
using LibA::String; //偏向使用LibA中的String
using LibB::Vector; //偏向使用LIbB中的Vector
//其他实体
}
- 当然如果在LibC中声明了String或者Vector的话就没有LibA和LibB什么关系了。
- 如果我们想要使用被重载覆盖的LibA::Vector和LibB::String.除了加上作用域解析符之外,可以使用typedef的形式和继承的形式。
template<class T>class A_Vector:public LibA::Vector<T>{...};
typedef LibB::String B_String;
- 对标准C库继续支持。以printf为例,在C头文件中使用std命名空间将stdio.h进行包裹。
//stdio.h
namespace std
{
int printf(const char*...);
}
using namespace std; //包含头文件后不需要再重复这句话了
//...
#include <cstdio> //对新库也进行包含
using std::printf;
C++中提供了新的头文件,头文件中没有使用using指令,这使不希望全部的标准库实体都隐式的使用成为了可能。
//cstdio.h
namespace std
{
int printf(const char* ...);
//...
}
- 命名空间是开放的,你可以在不同的文件中或同一个文件中的不同地方给命名空间加新的成员。也非常推荐使用这种方法将命名空间进一步分类,而不是将所有的实体都放到一个大块的命名空间中。
//file1.c
namespace A
{
int a;
}
//其他代码
namespace A
{
int b;
}
//file2.h
namespace A
{
int c;
}
C++命名空间学习笔记的更多相关文章
- PHP命名空间学习笔记
命名空间的支持版本:PHP 5 > 5.3.0,PHP 7 . 什么是命名空间 从广义上来说,命名空间是一种封装事物的方法.在很多地方都可以见到这种抽象概念.例如,在操作系统中目录用来将相关文件 ...
- php命名空间学习笔记。
为什么要用命名空间? 在PHP中,命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题: 用户编写的代码 与 PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲 ...
- [原创]java WEB学习笔记98:Spring学习---Spring Bean配置及相关细节:如何在配置bean,Spring容器(BeanFactory,ApplicationContext),如何获取bean,属性赋值(属性注入,构造器注入),配置bean细节(字面值,包含特殊字符,引用bean,null值,集合属性list map propert),util 和p 命名空间
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- thinkphp学习笔记8—命名空间
原文:thinkphp学习笔记8-命名空间 新版本(3.2)中采用命名空间的方式定义和加载类库文件,解决多个模块之间的冲突问题,并实现了更加高效的自动加载机制. 需要给类库定义所在的命名空间,命名空间 ...
- TypeScript学习笔记(八):1.5版本之后的模块和命名空间
我之前有写过TS1.5版本之前的“模块”的笔记:TypeScript学习笔记(七):模块 但是TS这里的模块和在ECMAScript 2015里的模块(即JS原生支持了模块的概念)概率出现了混淆,所以 ...
- Theano 学习笔记(一)
Theano 学习笔记(一) theano 为什么要定义共享变量? 定义共享变量的原因在于GPU的使用,如果不定义共享的话,那么当GPU调用这些变量时,遇到一次就要调用一次,这样就会花费大量时间在数据 ...
- [原创]java WEB学习笔记109:Spring学习---spring中事物管理
博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好 ...
- [原创]java WEB学习笔记105:Spring学习---AOP介绍,相关概念,使用AOP,利用 方法签名 编写 AspectJ 切入点表达式
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- SpringNet学习笔记一
---恢复内容开始--- 最近看了园子里的大神分享的springnet框架的知识,感觉挺不错的,自己闲下来也研究研究springnet.这几天看了springnet容器的基础篇IOC和AOP,也有点个 ...
随机推荐
- Structs复习 访问web元素
Structs帮我们在action和http里建立了联系 主要有四种方式 我们主要用第二种(IOC 依赖容器注入 ) Jar包 web.XML <?xml version="1.0&q ...
- SqlServer 中 for xml path 相关
表结构: typename varchar(50) typedesc varchar(50) 示例 SQL 语句: SELECT '{"'+TypeName, '":"' ...
- C# 根据Excel生成树
需求: 根据Excel生成树,Excel的某些节点为属性节点, 如: 列(桩号.构件编码.测试属性1) 是列(分项工程名称) 的属性,非节点. 列(桩号.构件编码.测试属性1) 以属性的方式存在 导入 ...
- C# 13位时间戳转换成标准时间C#代码
原地址:https://www.cnblogs.com/yixuehan/p/5559244.html /// <summary> /// 时间戳转换成标准时间 /// </summ ...
- Python之路 - Socket实现QQ聊天
Python之路 - Socket实现QQ聊天 介绍
- h5+css 垂直导航菜单
http://blog.csdn.net/baidu_32731497/article/details/51814427
- akka共享内存
Akka共享内存 Akka中的共享内存是基于Actor模型的,Actor模型提倡的是:通过通讯来实现共享内存,而不是用共享内存来实现通讯,这点是跟Java解决共享内存最大的区别,举个例子: 在Java ...
- 11.14java课堂测试
源代码: import java.*; import java.util.*; import java.io.File; import java.io.FileInputStream; import ...
- c# list批量添加 对象 到数据库的sql
public void AddList(List<ProModule.Model.pro_manifest_item> list) { Hashtable SQLStringList = ...
- 判断元素16种方法expected_conditions
前言 标签(空格分隔): 判断元素 经常有小伙伴问,如何判断一个元素是否存在,如何判断alert弹窗出来了,如何判断动态的元素等等一系列的判断,在selenium的expected_condition ...