C++学习 之 初识命名空间
声明:
本人自学C++, 没有计算机基础,在学习的过程难免会出现理解错误,出现风马牛不相及的现象,甚至有可能会贻笑大方。 如果有幸C++大牛能够扫到本人的博客,诚心希望大牛能给予批评与指正!不胜感激!
学习的过程分为初识、入门、进阶三个阶段。
因为对C++没有什么了解,这样的学习设定可能也有失准确性。望兄弟们多指点。谢谢!
目录:
科普:标识符、作用域
1. 命令空间出现的由来及什么是命名空间及全局命名空间
2. 命令空间的调用
3. 定义、添加命名空间及成员
4. 嵌套命名空间
5. 未命名的命名空间
回顾
科普:
在学习命名空间时啊,有这么一个概念一定要先弄明白。不然你会学得和尚摸不着头脑的。这个就是标识符。
标识符:
标识符是用于表示以下内容之一的字符序列:
对象或变量名称
类、结构或联合名称
枚举类型名称
类、结构、联合或枚举的成员
函数或类成员函数
typedef 名称
标签名称
宏名称
宏参数
说简单一点,标识符就是指的对象、变量、类、结构等的名字
标识符的组成规则:第一个字符必须是字母或者是下划线, 除了第一个字符外,由字符、数字、下划线组成
作用域:
通常来说,一段程序代码中某个标识符的使用都是有范围限制的,而这个标识符在哪个代码范围内可以被使用就是这个标识符的作用域。
对于对象而言(其他也是一样的),比如在main函数中,对象的作用域为它所在的最近的一对花括号内。全局的对象的作用域为声明之后的整个文件(单个文件内,不涉及多文件的工程)。
我们拿个变量来举个例子:
halberd:~ # cat scope1.cpp
#include <iostream>
using std::cout;
using std::endl;
int a=2; class test {
public:
test() {
cout << \"Testing varialbe \\\"a\\\" is used out of main()!\" << endl;
cout << \"Testing variable \\\"a\\\" is :\" << a << endl;
test1();
}
static void test1() {
cout << \"Testing varialbe \\\"b\\\" cout not be used out of main()!! \" << endl;
//cout << \"Testing variable \\\"b\\\" is \" << b << endl; //关键是这一行!请注意被注释的情况下跟没被注释的情况
}
};
static test test; int main()
{
int b=3;
cout << \"variable \\\"a\\\" be used in main is :\" << a << endl;
cout << \"variable \\\"b\\\" be used in main is :\" << b << endl;
return 0;
}
halberd:~ # g++ scope1.cpp -o scope1
halberd:~ # ./scope1
Testing varialbe "a" is used out of main()!
Testing variable "a" is :
Testing varialbe "b" cout not be used out of main()!!
variable "a" be used in main is :
variable "b" be used in main is :
a定义在{}外,b定义在{}内。
我们看到上面程序代码编译正常,执行正常,a 可以在不同{}内调用,b也可以在{}内调用,但是是否可以在不同{} 内调用呢?
再看下取消注释行后的情况 :
halberd:~ # cat scope1.cpp
#include <iostream>
using std::cout;
using std::endl;
int a=2; class test {
public:
test() {
cout << \"Testing varialbe \\\"a\\\" is used out of main()!\" << endl;
cout << \"Testing variable \\\"a\\\" is :\" << a << endl;
test1();
}
static void test1() {
cout << \"Testing varialbe \\\"b\\\" cout not be used out of main()!! \" << endl;
cout << \"Testing variable \\\"b\\\" is \" << b << endl; //关键是这一行!请注意被注释的情况下跟没被注释的情况
}
};
static test test; int main()
{
int b=3;
cout << \"variable \\\"a\\\" be used in main is :\" << a << endl;
cout << \"variable \\\"b\\\" be used in main is :\" << b << endl;
return 0;
}
halberd:~ # g++ scope1.cpp -o scope1
scope1.cpp: In static member function ‘static void test::test1()’:
scope1.cpp::: error: ‘b’ was not declared in this scope
cout << "Testing variable \"b\" is " << b << endl;
这里我们看到,编译时出现错误 。提示在b在作用域中没有被声明。从这里我们证明了,b 只能在被声明的那个{}内使用,出了这部分代码区域,程序就无法识别了。
上面这个例子中 标识符 a 的作用域是整个文件,无论是在main外还是main函数内都可以使用,标识符 a 的使用在此段代码文件中是没有限制的,也就是说 标识符 a的作用域是整个文件。
而b的可用范围是在main 函数的{}内部,也就是说b的作用域在main 函数的{}内。当在main函数外部 调用时,我们发现编译时无法通过,提示:error: ‘b’ was not declared in this scope('b'在该作用域中没有被声明)。那是在哪个作用域中没有声明呢?就是test1() 的花括号{}内了。
所以经过我们的测试,是否可以这样说呢:
单个文件内作用域最大范围是整个代码文件,最小范围是标识符所在的花括号内的代码范围,
为什么特别说明是单个文件内呢?因为随着我们学习知识越来越多,到时候会遇到作用域延伸的情况,比如某个标识符通过extern 被引用到另外一个文件中,这样这个标识符的作用域就得到了延伸。等我们入门以后再来学吧。这里只需要意识到还有其他情况 就可以了。
1. 命名空间的由来、什么是命名空间及全局命名空间
在认识什么是命名空间前,我们先来了解下,为什么会出现命名空间这个东西。
在了解前,我们先想想,我们上学的时候,是不是会经常遇到有同名同性的同学啊?我相信有不少人会有这种经历的。那么我们先假设下,在没有分专业,没有分班 级前,开了一次全校大会,大会上校长激昂陈词,要叫一个学生A上台,至于为啥要叫上台,叫上台干嘛我们不考虑,只想象下这个场景,而这个学校里有10个同 学叫A,校长这一,台下呼啦站起10个同学,校长是不是会蒙掉啊?哈哈
但是,校长了解到他想叫的这个同学是哪个院校哪个专业哪个班级的,校长找这个人的时候根据这些信息是不是就不会出现上面的情况了?校长把同学A的院校、专业、班级信息一起说出来,就避免了人名冲突的问题。
在C++开发过程中,也会遇到类似的情况,相同的标识符在同一个作用域中声明了多次,当对程序进行编译时就会出现冲突,编译器不知道应该 使用哪个标识符,这种冲突,在C++叫被叫作 Namespace Pollution。那为了避免或者说为了解决 这个问题,我们需要给这些标识符加上一些附加信息:“院校”、“专业”、“班级”,这些附加信息对应我们的代码文件中,就是命名空间了。
现在我们知道命名空间是怎么来的了,那我们怎么理解这个命名空间呢?我觉得应该从两个角度来理解,一个是组成,一个是作用。
同样的比喻,“班级”是一个命名空间,那班级里有什么呢?班级里是不是都是人啊?有教授,有助教,有男同学,有女同学等。那么我们就可以这样理解:从命名 空间的组成角度来讲,命名空间就是一个集合体,什么东西的集合体呢?
命名空间是标识符及其声明部分的集合。
那么从命名空间的作用角度又该怎么理解呢?
我们知道了,命名空间是用来避免标识符声明冲突的情况。那命名空间本身是以什么角色出现的呢?其实我们可以这样理解,命名空间就是一个作用域。C++程序 代码,由不同的作用域组成,在单个作用域中有不同的标识符,不同的作用域中有存在相同标识符的情况,当我们希望指定某个标识符时,其中一种方式就是使用命 名空间。也就是说,命名空间本身就是一个作用域。
综上,我们知道了,命名空间本身是一个作用域,由标识符及其声明部分组成。
那我们怎么理解全局命名空间呢?
《C++ Primer》中这样写道:
Names defined at global scopenames declared outside any class, function, or namespaceare defined inside the global namespace. The global namespace is implicitly declared and exists in every program. Each file that defines entities at global scope adds those names to the global namespace
按照我的理解,全局命名空间,是隐式定义的命名空间,没有明确的名字,整个程序里可以共用的(函数、类、命名空间以外)声明代码,都是声明在全局命名空间的。
使用全局命名空间中的标识符,需要使用作用域标识符“::”
2. 命令空间的调用
哎呀,我们搞明白了什么是命名空间,现在来学学怎么用吧。我们要学以至用,学了不用不成了纸上谈兵了么……嘿嘿
命名空间有三种用法(以std为例):
| 调用方式 | 优点 | 缺点 | 推荐 |
| using namespace std; | 使用方便,无需重复指定,下次使用时不需要指定命名空间 | 整个命名空间全部对文件开放,容易引起标识符的冲突问题 | 否 |
| using std:cout; | 定位精准,无需重复指定,下次使用时不需要指定命名空间,不容易出现标识符冲突问题。 | 未知 | 是 |
| std::cout; | 定位精准, | 下次使用需要重新指定命名空间但是书写繁琐 | 否 |
关于调用命名空间关键词、符号的说明:
using: 指示符,当using 作为指示符使用时,后面必须跟namespace 关键词,如果后面跟的不是已声明的命名空间,会报错。当然,using 还有其他功能,如重新声明基类成员,这个后面再研究吧。一下子吃不透那么多东西。
:: : 作用域限定符,它的作用是说明“::”后面所跟的标识符所在的作用域,它是同namespace一同出现的,用于避免标识符冲突的问题。 如:std::cout ,说明标识符cout 是作用域std中的cout,程序发现了"::"就会到该限定符前的作用域中去查找该标识符的声明代码,以确定标识符的功能。
如果该限定符前面什么都没有呢?那程序就会从全局作用域中查找该标识符的定义、声明。(什么时候会用到呢?蛋疼啊~知道的太少,实在想不出场景~望大神能给个例子,救人一命胜造七级浮图啊~教人一卷胜过吃饭千碗,功德无量!!感谢老戴提示:类静态成员和函数时会用到。等 以后学到类和函数时再研究)
3. 定义、添加命名空间及成员
折腾好几天,终于算是对命名空间是个什么东西了,下面我们来学习定义命名空间吧~激动人心的时刻来临啦~
3.1 定义命名空间的语法结构
namespace namespace_name {
………… //声明部分
}
在具体编写前,我们要先了解定义命名空间有哪些限制:
a) 由于命名空间只是作用域的一种表现形式,因此也需要遵守作用域中标识符的命名规则:作用域中的名字在该作用域中只能是唯一的,不然会出现冲突 。命名空间也一样,在一个命名空间中,名字也必须 是唯一的。
b) 命名空间可以在全局作用域或者其他作用域中定义,但是不能在函数、类结构内部定义
c) 命名空间作用域不能以分号结束
那命名空间允许我们定义哪些内容呢? 嘿嘿,只要是可以出现在全局作用域中的任意声明都可以在命名空间中定义,如类、变量及初始化、函数及定义、模板及其他命名空间(注意没有?命名空间中还可 以有命名空间呢,也就是说命名空间其实是可以嵌套的,后面会对这一点进一步了解。)
来来来,我们来写一个灰常简单的命名空间,先享受一次会写命名空间的乐趣,嘿嘿。
代码如下 :
#include <iostream>
using std::cout;
using std::cin;
using std::endl; namespace halberd_ns {
int a = 1;
} int a = 2; int main()
{
cout << \"This variable is from namespace halberd_ns: a=\"<<halberd_ns::a<<endl;
cout <<\"This variable is from global namesapce :: a=\"<<a<<endl;
return 0;
}
编译:
halberd:/home/C++/codes # g++ ./namespace_.cpp -o namespace_
执行:
halberd:/home/C++/codes # ./namespace_
This variable is from namespace halberd_ns: a=1 //从这里我们看到命名空间中变量的定义及初始化成功了,并在命名空间外成功调用命名空间中的变量
This variable is from global namesapce :: a=2
哈哈,成功了。
3.2 合并命名空间(2014-06-30 补充)
其实C++命名空间,可以分开写的。可以在同一个文件里分别声明不同功能的部分,也可以分到几个文件里声明。下面我们做个实验:
在上一小节,我定义了一个命名空间:halberd_ns . 下面,我们以这具为基础,进行实验。首先在文件内部再声明同名命名空间:
halberd:/home/C++/codes # cp namespace_.cpp ./namespace_1.cpp
halberd:/home/C++/codes # vi namespace_.cpp
halberd:/home/C++/codes # cat namespace_1.cpp
#include <iostream>
using std::cout;
using std::cin;
using std::endl; namespace halberd_ns {
int a = ;
} int a = ; namespace halberd_ns { //新声明命名空间 --看看是什么作用
int b = ;
} int main()
{
cout << \"This variable is from namespace halberd_ns: a=\"<<halberd_ns::a<<endl;
cout <<\"This variable is from global namesapce : a=\"<<a<<endl;
cout <<\"This variable is used to test namespace consolidation: b=\" <<halberd_ns::b<<endl; //验证我们的猜想
return ;
}
看上面的修改的部分,一个是新声明了一个命名空间halberd_ns,然后添加一行,用以验证两人个命名空间是否同时生效。
看下结果:
halberd:/home/C++/codes # g++ ./namespace_1.cpp -o namespace_1
halberd:/home/C++/codes # ./namespace_1
This variable is from namespace halberd_ns: a=1
This variable is from global namesapce : a=2
This variable is used to test namespace consolidation: b=3
我们看到:
第一,原命名空间没有被覆盖,原来的声明还有效。
第二,声明已存在 的命名空间,会将两个命名空间的内容合并起来,同时生效,这就相当于命名空间的合并。
下面我们再做个实验,我们在不同的文件中声明同一个命名空间,看会是什么效果呢:
alberd:/home/C++/codes # vi attach_namespace.cpp
halberd:/home/C++/codes # cat attach_namespace.cpp
#include <iostream>
namespace halberd_ns{
int c=;
}
halberd:/home/C++/codes # vi namespace_1.cpp
halberd:/home/C++/codes # cat namespace_1.cpp
#include <iostream>
#include \"attach_namespace.cpp\"
using std::cout;
using std::cin;
using std::endl; namespace halberd_ns {
int a = ;
} int a = ; namespace halberd_ns {
int b = ;
} int main()
{
cout << \"This variable is from namespace halberd_ns: a=\"<<halberd_ns::a<<endl;
cout <<\"This variable is from global namesapce : a=\"<<a<<endl;
cout <<\"This variable is used to test namespace consolidation: b=\" <<halberd_ns::b<<endl;
cout <<\"This variable is used to test outer file\'s namespace consolidattion: c=\" <<halberd_ns::c<<endl;
return ;
}
halberd:/home/C++/codes # g++ ./namespace_1.cpp -o namespace_1
halberd:/home/C++/codes # ./namespace_1
This variable is from namespace halberd_ns: a=1
This variable is from global namesapce : a=2
This variable is used to test namespace consolidation: b=3
This variable is used to test outer file's namespace consolidattion: c=4
从上面的实验,我们看到,命名空间halberd_ns, 分别在attach_namespace.cpp 中声明一次,并在namespace_1.cpp中声明两次。文件attach_namespace.cpp 被引用到namespace_1.cpp中,命名空间的声明也同时被引用过来。而且命名空间中的声明,也可以正常使用。
那么,我们可以这样说:
1) 命名空间的声明、定义可以是不连续的,一个命名空间可以分散到多个文件中,或者在同一个文件中的不同部分进行分别声明或者定义。
2) 同名命名空间的(非重复)声名、定义,可以累积、合并,在外界看来,就如同是只经过一次声明一样。
3.3 添加命名空间成员(待补充)
(感觉跟3.2 内容有些重复,但是确实是一块新东西。跟类相关,与老戴讨论半天,也没弄明白。先放一放吧。等 学完类再来补充这一块)
4. 嵌套命名空间(2014-07-01 补充)
所谓的命名空间嵌套,其实就是在命名空间作用域内再定义一个命名空间。格式类似如下:
namespace halberd_ns {
namespace halberd_ns1{
//definitions
……
}
}
来来来,咱做个实验:
点击(此处)折叠或打开
#include <iostream>
using std::cout;
using std::cin;
using std::endl; namespace halberd_ns {
int a = ;
namespace halberd_ns_nested{
int b=;
}
} int main()
{
cout << \"This variable is from namespace halberd_ns: a=\"<<halberd_ns::a<<endl;
cout <<\"This variable is from nested namesapce halberd_ns_nested:: b=\"<<halberd_ns::halberd_ns_nested::b<<endl;
return ;
}
编译:
linux-emf1:/home/C++/codes # g++ ./namespace_nested.cpp -o namespace_nested
执行进行验证:
linux-emf1:/home/C++/codes # ./namespace_nested
This variable is from namespace halberd_ns: a=1
This variable is from nested namesapce halberd_ns_nested:: b=2
嘿嘿,果然又成功了。怎么样,咱学起来,不是很难吧。虽然遇到有不会的,无法 理解的。但是,我们一直在进步呢。对吧。只要在前进,就会离成功越来越近。
5. 未命名的命名空间(2014-07-01 补充)
未命名的命名空间,啥意思,不用我多说了吧,就是没有名字嘛。
定义格式如下:
namespace {
// definitions
……
}
前面我们了解过全局命名空间,还有印象不,没有的话,到本页最上面看。这全局命名空间有相似之处。你知道是啥不?对啦,就是没有名字。那他们之间有什么区别呢?弄清楚这个区别,咱这个没有名字的命名空间的特点也就了解了:
| 对比 | 全局命名空间 | 未命名的命名空间 |
| 是否允许嵌套 | 否 | 是,允许嵌套在其他命名空间内 |
| 使用范围 | 全局,可在工程内任意文件中引用成员 | 局部,只能在一个文件内的局部作用域中使用,不能跨文件使用 |
| 引用成员方式 | 使用作用域限定符"::" | 直接使用成员名,不可以使用作用域限定符"::" |
| 定义方式 | 隐匿定义(自动定义,每个工程都有自己的默认的一个全局命名空间) | 显式定义 |
未命名的命名空间其他特点:
1) 未命名的命名空间中定义的变量在程序开始时创建,在程序结束时释放一直存在
2) 如果在最外层定义未命名的命名空间,成员名不能与全局作用域中的名字一样,为啥?会跟全局命名空间的成员冲突呗
回顾:
我们对于命名空间都学习了哪些东西呢?
1. 什么是标识符和作用域
2. 命名空间的由来
3. 什么是命名空间(从作用及组成两方面来考虑)
4. 命名空间的调用
5. 命名空间的定义
6. 命名空间合并
7. 添加命名空间成员(待补充)
8. 未命名的命名空间
9. 嵌套命名空间
C++学习 之 初识命名空间的更多相关文章
- SSH 框架学习之初识Java中的Action、Dao、Service、Model-收藏
SSH 框架学习之初识Java中的Action.Dao.Service.Model-----------------------------学到就要查,自己动手动脑!!! 基础知识目前不够,有感性 ...
- thinkphp学习笔记8—命名空间
原文:thinkphp学习笔记8-命名空间 新版本(3.2)中采用命名空间的方式定义和加载类库文件,解决多个模块之间的冲突问题,并实现了更加高效的自动加载机制. 需要给类库定义所在的命名空间,命名空间 ...
- DotNetty网络通信框架学习之初识Netty
p{ text-align:center; } blockquote > p > span{ text-align:center; font-size: 18px; color: #ff0 ...
- C++学习 之 初识头文件
声明: 本人自学C++, 没有计算机基础,在学习的过程难免会出现理解错误,出现风马牛不相及的现象,甚至有可能会贻笑大方. 如果有幸C++大牛能够扫到本人的博客,诚心希望大牛能给予 ...
- C++学习 之 初识C++
声明: 本人自学C++, 没有计算机基础,在学习的过程难免会出现理解错误,出现风马牛不相及的现象,甚至有可能会贻笑大方. 如果有幸C++大牛能够扫到本人的博客,诚心希望大牛能给予 ...
- TensorFlow学习(1)-初识
初识TensorFlow 一.术语潜知 深度学习:深度学习(deep learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法. 深度学 ...
- 学习笔记:java并发编程学习之初识Concurrent
一.初识Concurrent 第一次看见concurrent的使用是在同事写的一个抽取系统代码里,当时这部分代码没有完成,有许多的问题,另一个同事接手了这部分代码的功能开发,由于他没有多线程开发的经验 ...
- Java学习点滴——初识Java
基于<Java编程思想>第四版 前言 "程序就是算法加数据结构",而算法就是控制语句加操作符,编写一个程序就是使用控制语句加操作符去操作数据结构,因此我从Java的控制 ...
- Xamarin.Forms学习之XAML命名空间
大家好,我又悄咪咪的来了,在上一篇的Xamarin文章中简单介绍了Xamarin的安装过程,妈蛋没想到很多小朋友很感激我,让他们成功的安装了Xamarin,然后......成功的显示了经典的两个单词( ...
随机推荐
- GitHub的本地与远程
首先要有一个github账户(这不是废话吗) 在linux中先安装git arch linux : pacman -S git 在终端里输入 ssh-keygen ##一直默认就可以了 将公钥加入到G ...
- TCP的ACK原理和延迟确认机制
某天晚上睡觉前突然想到 tcp的ACK确认是单独发的还是和报文一起发的,下面看一下别人的解答 一.ACK定义TCP协议中,接收方成功接收到数据后,会回复一个ACK数据包,表示已经确认接收到ACK确认号 ...
- 通过jenkins api远程调用job
curl http://jenkins地址/job/job_name/config.xml --user username:token
- es实战之数据导出成csv文件
从es将数据导出分两步: 查询大量数据 将数据生成文件并下载 本篇主要是将第二步,第一步在<es实战之查询大量数据>中已讲述. csv vs excel excel2003不能超过6553 ...
- 箭头函数 -ES6
1)函数参数只有一个:可以省略 ( ) var f = a => a 等同于 var f = function (a) { return a } 2)函数内部语句只有一个:可以省略 { ...
- Http中的三种请求处理模式(MPM)的区别
MPM---包括基于事件/异步,线程化和预分叉 MPM(multi-processing module)多种请求处理模式,分为三种工作模式: prefork worker event prefork- ...
- abd常用命令
1.adb install -r 包名 ----------安装包,-r 替换原有apk安装 2.adb uninstall -k 包名 ---------------卸载包, ...
- 前端Web浏览器基于Flash如何实时播放监控视频画面(三)之使用ffmpeg‘推流’
本片文章只是起到抛砖引玉的作用,能从头到尾走通就行,并不做深入研究.为了让文章通俗易懂,尽量使用白话描述. 0x001: 下载ffmpeg 开源免费的推流软件有很多,这里以 ffmpeg 为例.ffm ...
- XML -- 为什么选择XML?
1.XML是什么,主要功能? XML全称(EXtensible Markup Language),是可扩展性标记语言. XML主要功能是用来传输和存储数据.它就是一种纯文本.只要程序能访问纯文本就能访 ...
- Java进阶知识05 Hibernate联合主键之Annotation(注解)和XML实现方式
1.Hibernate联合主键(Annotation实现) 1.1.单列主键 1.1.1.为什么要有主键? //唯一确定一条记录 1.1.2.一个表能否有多个主键? //不能 1.1.3. ...