2021-07-12:C++基础知识01

C++基础知识01

Section01:双冒号运算符与做用域

Subsection-1.1:做用域

在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

Subsection-1.2:双冒号运算符使用命名空间

所谓命名空间,是管理命名列表而生的。之因此须要对命名进行管理,那是为了在 “迭代开发” 或者是多人 “合做开发” 中,避免 “命名冲突” 的危害!函数

如何调用命名空间的变量和函数呢?那就是用双冒号做用域限定符!看一个需求:假设在游戏开发中,先开发了英雄露娜的操做,例如:***、大招;随后又开发了英雄澜的操做,其中也有***和大招,请问怎么实现比较好呢?给出示例程序:测试

(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

Subsection-1.3:命名空间的编程技法总结

在上面,咱们了解了,命名空间的设计目的、设计场景。以及以游戏的迭代开发来了解命名空间的使用!以及简单的例子,了解到命名空间能够加入任意C++中的名称元素(变量、函数、类、结构体等……)。可是,命名空间仍是其余的一些有用的技法:

技法1:可扩充的命名空间

以游戏开发为例,澜这个英雄以前有普通***、放大招的功能,如今要更新一个回城的功能。而此前的代码不想改动了,能否扩充命名空间呢?以下:

新的的头文件:

#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;
}

运行后的结果:
在这里插入图片描述
总结:
命名空间能够扩展,每次扩展不是覆盖原有的命名空间,而是把旧的命名空间和新的命名空间合并!

技法2:可匿名的命名空间

这个技法其实没啥大用,就怕有的程序员使用了,而你不知道,就会踩坑!

若是命名空间写为:

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;
}

运行的结果是:
在这里插入图片描述

技法3:可嵌套的命名空间

命名空间是能够嵌套的!这通常得是很大的项目了!不然,其实命名空间自己也是一个比较大,我的认为是 “仅次于全局做用域” 的存在了!例如:

#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;
}

运行的结果是:

在这里插入图片描述

技法4:存在别名的命名空间

这个其实很简单,只须要用一个新的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;
}

而后输出的结果,和上一个技法的例子,输出一致!

Section02:using关键字用法

Subsection-2.1:用using定义类型别名

对比C语言的 #define 以及 typedef

若是用 #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

C++11中,用using定义类型别名

若是用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

Subsection-2.2:用using进行声明

用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的声明和普通的声明,优先级一致!此时,编译器就不知道你究竟是要访问谁了?!

Subsection-2.2:用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;

Subsection-2.3:总结using的用法

  1. 在C++11里,能够用来给类型作别名
  2. 能够进行声明,可是要注意二义性,由于其和当前做用域的声明级别同样
  3. 能够编译指令,至关于告诉编译器须要编译命名空间的内容!级别低于当前做用域的声明,若是须要调用命名空间,须要用做用域限定符!
相关文章
相关标签/搜索