在C/C++的做用域中,讲究一个就近原则。也就是说,在局部和全局都对某个变量进行了定义时,咱们的访问/调用会去操做 “最近” 的那个变量!例以下面的例子:ios
#include <iostream> using std::cout; using std::endl; int atk = 200; void test() { int atk = 100; cout << "Local atk = " << atk << endl; } int main() { test(); return 0; }
你们能够先猜一下运行的结果?没错,就是100!这就是体现了刚刚提到的 “就近原则”。那么,若是在这种状况下,咱们仍是想要去读甚至写全局的变量atk呢?怎么办呢?看看下面的程序:程序员
#include <iostream> using std::cout; using std::endl; int atk = 200; void test() { int atk = 100; cout << "Local atk = " << atk << endl; cout << "Global at = " << ::atk << endl; ::atk = 800; cout << "Global at = " << ::atk << endl; } int main() { test(); return 0; }
而后再看一下运行效果:编程
这就是双冒号运算符,改变做用域的效果。在没有指定命名空间的时候,做用域就是全局做用域!ide
所谓命名空间,是管理命名列表而生的。之因此须要对命名进行管理,那是为了在 “迭代开发” 或者是多人 “合做开发” 中,避免 “命名冲突” 的危害!函数
如何调用命名空间的变量和函数呢?那就是用双冒号做用域限定符!看一个需求:假设在游戏开发中,先开发了英雄露娜的操做,例如:***、大招;随后又开发了英雄澜的操做,其中也有***和大招,请问怎么实现比较好呢?给出示例程序:测试
(1)先看看开发Lunar的过程spa
头文件(实现定义,注意定义就要在命名空间里定义):设计
#pragma once #ifndef __LUNAR__ #define __LUNAR__ #include <iostream> using namespace std; namespace lunar { void goAtk(); void goUltimate(); } #endif
CPP文件(负责实现):code
#include "Lunar.h" void lunar::goAtk() { cout << "Lunar 释放***!" << endl; } void lunar::goUltimate() { cout << "Lunar 释放大招!" << endl; }
(2)随后开发Lan的过程blog
头文件:
#pragma once #ifndef __LAN__ #define __LAN__ #include <iostream> using namespace std; namespace lan { void goAtk(); void goUltimate(); } #endif
CPP文件:
#include "Lan.h" void lan::goAtk() { cout << "Lan 释放***!" << endl; } void lan::goUltimate() { cout << "Lan 释放大招!" << endl; }
总结命名空间:
命名空间能够存放C++任何名称,例如变量、结构体、类、函数等等……,而且,命名空间无需加分号!可是命名空间必须放到全局做用域!不能够写入任何一个函数、类!
举一个更简单的例子,要访问两个命名空间的变量和函数:
#include <iostream> using std::cout; using std::endl; namespace ns1 { int a; void func(int a) { cout << a << endl; } } namespace ns2 { int a; void func(int a) { cout << a << endl; } } int main() { ns1::a = 100; ns2::a = 200; ns1::func(ns2::a); ns2::func(ns1::a); return 0; }
它输出的结果,就显而易见了,是:
200
100
在上面,咱们了解了,命名空间的设计目的、设计场景。以及以游戏的迭代开发来了解命名空间的使用!以及简单的例子,了解到命名空间能够加入任意C++中的名称元素(变量、函数、类、结构体等……)。可是,命名空间仍是其余的一些有用的技法:
以游戏开发为例,澜这个英雄以前有普通***、放大招的功能,如今要更新一个回城的功能。而此前的代码不想改动了,能否扩充命名空间呢?以下:
新的的头文件:
#pragma once #ifndef __LAN__ #define __LAN__ #include <iostream> using namespace std; namespace lan { void goAtk(); void goUltimate(); } namespace lan { void goHome(); } #endif
新的CPP文件:
#include "Lan.h" void lan::goAtk() { cout << "Lan 释放***!" << endl; } void lan::goUltimate() { cout << "Lan 释放大招!" << endl; } void lan::goHome() { cout << "Lan 回城!" << endl; }
测试程序:
#include "Lan.h" int main() { lan::goAtk(); lan::goUltimate(); lan::goHome(); return 0; }
运行后的结果:
总结:
命名空间能够扩展,每次扩展不是覆盖原有的命名空间,而是把旧的命名空间和新的命名空间合并!
这个技法其实没啥大用,就怕有的程序员使用了,而你不知道,就会踩坑!
若是命名空间写为:
namespace { int a, b; }
实际上等价于在当前文件,写了:
static int a; static int b;
就彻底等价于静态变量/函数,意味着只能在当前文件里使用了!好比下面的程序:
#include <iostream> using namespace std; namespace { void sayHello() { cout << "Hello World" << endl; } } int main() { sayHello(); return 0; }
运行的结果是:
命名空间是能够嵌套的!这通常得是很大的项目了!不然,其实命名空间自己也是一个比较大,我的认为是 “仅次于全局做用域” 的存在了!例如:
#include <iostream> using namespace std; namespace ns1 { int a = 1; namespace ns2 { int a = 2; } } int main() { cout << ns1::a << ' ' << ns1::ns2::a << endl; return 0; }
运行的结果是:
这个其实很简单,只须要用一个新的namespace空间名去赋值一个旧的命名空间便可,例如:
#include <iostream> using namespace std; namespace ns1 { int a = 1; namespace ns2 { int a = 2; } } int main() { namespace newNS = ns1; cout << newNS::a << ' ' << ns1::ns2::a << endl; return 0; }
而后输出的结果,和上一个技法的例子,输出一致!
若是用 #define,则,其规则是:
#define 别名 所表明的名称
若是用 typedef,则,其规则是:
typedef 所表明的名称 别名
例如,分别用 #define 和 typedef 来重命名 unsigned int 和 long long,示例以下:
#include <iostream> #define uInt unsigned int typedef long long ll; int main() { uInt u = 102u; std::cout << u << std::endl; ll l = 999999999999; std::cout << l << std::endl; return 0; }
输出的结果是:
102
999999999999
若是用using来声明类型别名,则,其规则是:
using 别名 = 所表明的名称;
举个例子:
#include <iostream> using uInt = unsigned int; using ll = long long ; int main() { uInt u = 102u; std::cout << u << std::endl; ll l = 999999999999; std::cout << l << std::endl; return 0; }
其输出的结果,和上面的一致,都是:
102
999999999999
用using能够用来声明变量和函数,先看一个简单程序:
#include <iostream> int a = 10; namespace ns { int a = 20; } void test() { int a = 30; std::cout << a << std::endl; } int main() { test(); return 0; }
其输出结果是:
30
这是因为就近原则,再也不解释了~
而后,若是想要使用命名空间 ns 的变量 a 呢?能够有这种作法:
#include <iostream> int a = 10; namespace ns { int a = 20; } void test() { int a = 30; using ns::a; std::cout << a << std::endl; } int main() { test(); return 0; }
可是这样会带来报错:
缘由很重要:
using 进行声明的时候,会产生二义性!意味着会和当前做用域其余同名变量发送冲突!换句话说,using的声明和普通的声明,优先级一致!此时,编译器就不知道你究竟是要访问谁了?!
听起来很高级,其实就是为编译器补充了一个须要编译的块,这个块能够理解成一个盒子,盒子里包装这一个命名空间的所有内容!好比:
#include <iostream> int a = 10; namespace ns { int a = 20; } void test() { int a = 30; using namespace ns; std::cout << ns::a << std::endl; } int main() { test(); return 0; }
此时的输出结果,是:
20
须要注意的是:
编译了命名空间,只是让编译器对命名空间的内容完成编译,而其优先级是 低于 当前做用域的声明的!因而,仍是会进行就近原则!可是,咱们为了可以输出命名空间的变量,就和我们本文最开始讲的那样,显式调用命名空间的变量!其中关键的程序是:
using namespace ns; std::cout << ns::a << std::endl;