一、前言
大家好呀,我是 a n d u i n anduin anduin 。今天为大家带来了第一篇 C++ 文章,接下来 a n d u i n anduin anduin 会和大家一起学习 C++ ,共同进步,共同成长!
C++语言建立在C的基础之上。C++ 容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式等。这些使得 C++ 更加强大。
所以我们在 C++ 入门时,会学习很多琐碎的语法,这些实际上就是在对 C 添砖加瓦,和填 “坑”。
对于 C++ 的重点,其实有两方面,一块是我们入门结束后要学习的类和对象,还有一部分就是 stl 标准模板库。这些我在以后都会重点讲解。
而 C++ 的历史我也就不多说了,我们今天的重点是讲解知识,所以对历史有兴趣的小伙伴可以去查阅一下资料。
说了这么多,其实就是因为是 C++ 开篇哈哈哈,得把之后的内容交待一下,相信有的小伙伴已经迫不及待了,所以我们这就开始学习 C++ 入门第一篇 。
二、第一个 C++ 程序
在学习一门语言时,我们总是会先使用该语言编写 “hello world” 程序,意味着打开了新世界,所以在开始我们的 C++ 之旅前,我们也写个程序:
#include <iostream> using namespace std; int main() { cout << "hello world" << endl; return 0; }
这就是 C++ 的 hello world 写法,但是我们有没有考虑过他为什么能打印出 “hello world” ?
这里的 namespace 是什么,打印内容的那一句代码又是什么意思?这些可能我们可能都没想过。如果你对这些还不太了解,或是比较生疏,那么恭喜你,本文非常适合像你我这样的初学者,学习完这篇文章后,就可以大致想清楚这个程序的意思了。
三、C++ 关键字(C++98)
C++一共有63个关键字,其中包含 C语言的32个关键字。
接下来我们就来认识一下它们:
数量大约是我们学习 C 语言时的两倍。
我们这边就是见一见,之后文章中都会讲到~
四、命名空间
接下来我们讲解的就是 namespace 命名空间。
命名空间的作用:在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染。
对于 C 语言,是无法解决命名冲突的,举个例子:
rand 是 c 语言中取随机数的一个库函数名,在没有引头文件:#include <stdlib.h> 的情况下,我们可以使用 rand 来定义变量。
但是一旦引了头文件,就会发生报错:
这就是 命名冲突 。这种情况实际上很常见,比如定义变量时可能会和库里的名字冲突;在与他人合作时,可能多个人定义的名字之间也会冲突。
而通过命名空间,就可以轻松解决这个问题。
1、命名空间的定义
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对 {} 即可,{} 中即为命名空间的成员。
我们定义出的命名空间就像一个域,就像局部域和全局域一样,每个域之间不相互影响,我们可以把命名空间叫做命名空间域。
命名空间域只影响使用,不影响生命周期。
所以在不同的 namespace 中的成员就不会互相冲突。
命名空间有 四个特点 :
命名空间名字不受限定,可以随机取。
命名空间中可以定义变量/函数/类型,十分自由
命名空间可以嵌套定义
若同一工程中,命名空间名字相同,最终会被合并为一个命名空间,此时就几乎丧失了命名空间的作用,因为在这里面命名冲突存在的话依然会报错
下面逐个演示一下:
1、2特点:
namespace anduin // 名字就是我 { int val = 10; int solve() { int returnvalue = 20; return returnvalue; } }
3特点:
namespace anduin { int foo = 100; namespace guldan { int fooo = 1000; } }
4特点:
在工程中,用定义同样的命名空间:
在 .c
文件中包含头文件,编译运行时,两个命名空间就会合并:
这里就相当于用 tool.h 中的 print
函数将 test.h 中的 max
打印了出来。
2、命名空间的使用
命名空间的使用的关键为 域作用限定符 ::
,就是我们上面 4 特点中像个骰子一样的 ::
。
::
的左边为域,如果有命名空间域,则限定访问命名空间域中的内容,如果域左边为空,访问的就是全局域,会直接到全局范围内找 ::
右边的变量或其他。
举个 ::
访问全局域的例子 :
我们知道,C/C++ 为局部优先原则,默认先从局部找,但是 :: 就直接将域限定到了全局域,找到就使用,找不到就报错。所以打印的为全局的 a = 2 。
如果在命名冲突的情况下,就可以将冲突的部分放到不同的域,通过域作用限定符来访问命名空间,:: 左边就放命名空间,右边就是命名空间中的成员,通过这种方式来解决问题 ,我们再举个例子:
我们之前学过数据结构,知道链式队列中是要使用到链表的,假如此刻我们有两个头文件:
如果同时包含头文件,且定义相同类型名字 Node
的节点,通过命名空间就可以成功定义:
我们看到是可以成功定义的。
这里 struct 放命名空间的前面是因为冲突的是 Node
,struct
是一个前缀,::
修饰的是冲突的部分。
嵌套命名空间的使用 :
通过 ::
不断访问命名空间,找到 print
函数和 a
完成对数据的打印。
3、命名空间的三种展开方式
通过我们上面了解了命名空间的使用,其实发现有时使用很繁琐,需要不停的 :: 展开,所以命名空间还有别的展开方式。
命名空间一共有三种展开方式:
指定命名空间访问(就是我们上方的,使用一次展开一次)
全局展开
部分展开
对于这块的讲解呢,就可以回归我们第一个 C++ 程序的代码了。这就既能解决我们的疑惑,又能讲解知识点。
#include <iostream> using namespace std; int main() { cout << "hello world" << endl; return 0; }
实际上通过上面的学习我们可以知道:其实 std
就是一个命名空间,为了防止命名冲突,C++ 之父在发明是就给它包好了一个空间,就是 std
。
指定命名空间访问:
指定命名空间就是一个个展开嘛,直接上代码:
全局展开 :
全局展开就是 using namespace std ,直接将 std 在全局展开了,所以使用的时候就无须使用 :: 进行逐个展开,可以直接使用。
打个比方,比如没展开,就会直接在全局找这个变量;但是如果展开,就不仅在全局找,还会到命名空间找。展开相当于影响了编译时的查找规则。
但是实际上这种展开方式并不好,因为命名空间,就是为了防止冲突而建立。这边就相当于直接把命名空间拆开来了。
所以对于这种展开我认为在平常练习代码,或者是刷题时很好用,但是对于写工程就不适合了。所以在之后的讲解知识点的时候,我大多还是全局展开,但是小伙伴们需要注意区分一下使用场景。
部分展开 :
综合上面两种方案,还有一种就是部分展开,对命名空间某个常用成员进行展开,比如:
假如 cout 常用,我就部分展开 cout ;对于 endl 我就不进行展开,还是指定访问。
总结 :
命名空间的展开就是为了使用的方便,对于不同的情况有不同的展开方式:
写工程,写项目:常用的部分展开,不常用的局部展开,两者混搭,一切为了安全和严谨。
练习,刷题:用全局展开更加方便,一切为了效率和方便。