一、C++入门
1. 新建一个C++项目
首先,在学习C++之前,我们需要新建一个C++的项目。我们打开 Visual Studio(我在这里使用的是VS2022版本,用什么版本都没关系)。
我们选择 创建新项目 。
接着选择 空项目 ,然后点击 下一步 。
接着给项目命名,并选择把项目放到哪个目录下,最后点击 创建 。
然后在右侧(可能是左侧)找到 解决方案资源管理器 ,在此下面右击 源文件 ,然后按顺序选择 添加 , 新建项 。
最后,选择 C++文件 ,并给将要生成的源文件命名(后缀需要是.cpp哦)。
这样就有了一个C++的文件了,接下来就可以学习各种C++的知识啦。
2. C++关键字
在C++98版本中,总共有 63 个关键字。
在初学阶段,并不是所有的关键字我们都需要知道,而且很多关键字都是我们在学习C语言的时候就已经学过了的。我们只需要学到哪用到哪,需要什么学什么。久而久之,只要经常地去写C++程序,那么常用关键字是可能没问题的,不常用的关键字到时候查阅一下就行。
3. 命名空间
学习过程中,与实践相结合肯定会事半功倍的,我们先来执行一个C++程序。
#include<iostream> using namespace std; int main() { cout << "Hello World!" << endl; return 0; }
结果:
其中,被include包含 iostream 就是用于在控制台或文件之间进行输入和输出操作的头文件, cout 就是输出,类似于 printf ,先不展开细讲, endl 就是换行符。我们主要讲的就是第二行,using namespace std; 这行代码与命名空间的关系密不可分。
在C++中,为什么有命名空间呢?他的作用是什么?我们先来看一个C语言程序:
#include<stdio.h> int rand = 10; int main() { printf("%d\n", rand); return 0; }
学过C语言,或者说对C语言有一定理解的应该都知道这段代码的含义,让我们来看看结果:
答案是10,嗯,没有问题。那假如啊,我在上面代码的基础上再包含一个 stdlib.h 头文件呢?
#include<stdio.h> #include<stdlib.h> int rand = 10; int main() { printf("%d\n", rand); return 0; }
让我们再来看看结果:
欸~?怎么回事,咋就报错了?加上了 stdlib.h 头文件就报错了?我们知道,头文件在预处理阶段是会被展开的,而在刚刚新加的头文件当中,是存在一个叫做 rand 的函数,在定义全局变量 rand 时,与函数 rand 发生了命名冲突的,所以编译失败。
命名冲突的问题主要发生在
- 程序员 与 库
- 程序员 与 程序员
C语言并不能解决这种命名冲突的问题,唯一的解决方法就是。。。其中一个程序员重新取个名字。那问题来了,在同一个域当中不能定义同名变量(或其他),不同的域当中能不能定义同名变量呢?
#include<stdio.h> int x = 0; int main() { int x = 1; printf("%d\n", x); return 0; }
结果是:
当然是可以的,第一个 x 是全局域,第二个 x 是局部域。在调用的时候也是遵循 局部优先,就近原则 。我又双叒叕有问题了,假如哈,我想调用那个全局的 x ,有什么办法吗?
变量的调用遵循 局部优先 就近原则 。即先去调用局部域的变量,局部域找不到再去全局域找。
#include<stdio.h> int x = 0; int main() { int x = 1; printf("%d\n", ::x); return 0; }
来看看结果:
嚯~,还真是 0 ,真的访问到全局变量了,那个 :: 又是什么?其实这个由两个冒号组成的符号叫做 作用域限定符 。这个符号左边部分就是限定的作用域,如果不写,则默认为全局域。
:: 是作用域限定符,该符号左边则是被限定的作用域,调用也只在被限定的作用域内查找,如若不写,则默认为全局域。
C++中域的种类
- 全局域
- 局部域
- 命名空间域
- 类域
OK,咱们回到C++的命名空间部分。既然说,在同一个域内定义两个同名变量会报重定义的错,那假如我们真的需要定义两个全局变量 x ,怎么办呢?我们来学习一个关键字 namespace ,这个关键字的意思就是命名空间,用法有一点点向结构体,但他定义的不是结构体名称,而是命名空间的名称。
#include<stdio.h> namespace space1 { int x = 1; } namespace space2 { int x = 2; } int main() { printf("%d\n", space2::x); printf("%d\n", space1::x); return 0; }
我们使用 namespace 关键字创建两个个命名空间域,分别包住两个创建的变量 x ,我们看看结果:
结果还是一如既往的没毛病。那我问个问题,既然被命名空间域给包含了,那这俩 x ,还是全局变量吗?答案当然是还是全局变量,我们通过分析生命周期就知道,被命名空间包含了后并不会改变他的生命周期,改变的仅仅是 调用/访问 。
全局域 改变 生命周期 和 访问
局部域 改变 生命周期 和 访问
命名空间域 改变 访问
如果我们不去使用作用于限定符,还访问得到被命名空间域给包含的变量吗?
#include<stdio.h> namespace space1 { int x = 1; } namespace space2 { int x = 2; } int main() { printf("%d\n", x); printf("%d\n", x); return 0; }
来看看结果:
好像不行,为什么呢?那是因为编译器只会访问 当前局部域 和 全局域,访问不到就会报错。
编译器搜索原则 :
如果没有指定作用域:1. 当前局部域 -> 2. 全局域 两个都没搜索到则报错。
如果指定了作用域,则直接去作用域搜索,没搜索到则报错。
到这里,大家应该就知道了前面遇到的 rand 命名冲突的问题——我们只需要将 rand 放到一个命名空间域里面就可以了。至于命名空间名称冲突了怎么办,会发生什么问题,大家可以自己去尝试一下。
命名空间名重复的,编译器会将重复的命名空间给合并。
再来看看这个:
#include<stdio.h> namespace space1 { int x = 1; int ADD(int a, int b) { return a + b; } } namespace space1 { //int x = 2; struct Node { struct Node* next; int val; }; } int main() { printf("%d\n", space1::x); printf("%d\n", space1::ADD(1, 2)); struct space1::Node phead; return 0; }
OK,已经可以灵活运用啦。
命名空间内不仅仅可以定义变量,还可以定义函数,结构等等,只要是你能定义的,都能在命名空间内定义。
使用命名空间域内的结构体并不是space1::struct Node phead;
而是struct space1::Node phead;
结构体关键字在前面。
咱们继续回~,上次说到 using namespace std; 这,这句代码的作用是什么?我先说一种情景,比如说在某个文件下,非常频繁的使用某个命名空间域内的变量或者函数或者其他等等,我们每次访问和调用都需要加上命名空间名和作用域限定符,这也太麻烦了吧,于是乎,==using namespace == 这种方式就是展开命名空间,意思就是把访问权限打开,访问的时候就不需要指定作用域,直接访问就可以访问到。后面的 std 就是展开的命名空间是 std 这个命名空间。
using namespace:展开命名空间
用法:using namespace space1;
作用:使展开的命名空间的访问权限打开,不使用作用域限定符就可以访问。
*由于命名空间域包含的都是一些全局的东西,所以展开也就相当于恢复成全局。
嘛,至于为啥要展开 std 这个命名空间呢。原因是这个 std 大有来头,他是所有C++库的命名空间。也就是说,不加这个,你使用的所有C++库内的东西都需要加上 std:: 这样的东西,可谓是超级麻烦。演示一下:
#include<iostream> int main() { std::cout << "Hello World!" << std::endl; return 0; }
结果:
cout 和 endl 是C++标准输入输出库 iostream 的东西,要使用必须加上作用域限定符,代码短的还好,长的就会复杂很多了。
std是所有C++库的命名空间。
展开命名空间后仍然可以使用 std::
展开命名空间还是有弊端的,所以在日常练习中可以随意展开,在做项目的时候不建议展开。
未完待续
关于C++的知识还是非常非常多的,一篇是肯定不能介绍完的,要是对后续部分感兴趣可以关注我哦,我会尽快完成下一篇的。