【C++】凝视C++的第一眼
慕雪年华

从今天开始,正式步入C++学习的大门啦!

C++的博客主要是我个人学习的学习笔记,刚开始会记一些比较零碎的小知识点,可能没有之前C语言的博客那么系统化😂

笔记中可能有错误的地方,欢迎大家指出!

感谢你关注慕雪,欢迎来我的寒舍坐坐❄慕雪的寒舍

[TOC]

1.C++简介

你可以通过菜鸟教程网来学习C++👉传送门

C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。

C++ 被认为是一种中级语言,它综合了高级语言和低级语言的特点。

C++ 是由 Bjarne Stroustrup 于 1979 年在新泽西州美利山贝尔实验室开始设计开发的。C++ 进一步扩充和完善了 C 语言,最初命名为带类的C,后来在 1983 年更名为 C++。

C++ 是 C 的一个超集,事实上,任何合法的 C 程序都是合法的 C++ 程序。


2.C++关键字

C语言有32个关键字,C++在它们的基础上进行了扩容,共有63个关键字

asmelsenewthisautoenum
operatorthrowboolexplicitprivatetrue
breakexportprotectedtrycaseextern
publictypedefcatchfalseregistertypeid
charfloatreinterpret_casttypenameclassfor
returnunionconstfriendshortunsigned
const_castgotosignedusingcontinueif
defaultinlinesizeofvirtualstaticvoid
dolongstructwchar_tdeleteint
static_castvolatiledoublemutableswitchwhile
dynamic_castnamespacetemplate

如果你想查看它们的完整介绍,可以参考这篇教程👉传送门

这里只做一个基本的认识,后续会一一学习这些关键字


3.安装你的C++环境

  • 如果是windows系统,建议使用微软的VisualStudio,不要用dev这种老式编译器!
  • 如果是mac系统,可以使用Xcode(我没有mac不知道具体情况)
  • 如果是linux系统,需要安装G++编译器

利用我的树莓派,给大家演示一下linux环境安装G++编译器

下面这个语句是显示g++编译器的版本,如果你弹出了和我一样的页面,说明你的linux系统里面已经有这个编译器了

1
g++ -v

image

如果没有,我们可以使用下面这个语句来整一个g++

1
sudo apt-get install g++

你也可以用这个语句来验证你的G++是否已经安装

image

下载完成后,就可以愉快的使用你的G++编译器了

如果你的下载很慢,可能要考虑换源(即更换一个软件下载源)

树莓派实验室详细记录了国内比较好用的源,也有换源教程👉传送门


4.C++的输入输出

4.1cout和cin样例

老规矩,向这个世界问个好吧!

1
2
3
4
5
6
7
#include <iostream>
using namespace std;
int main()
{
cout << "Hello, world!" << endl;
return 0;
}

image

这里出现的endl是“end of line”的缩写,结束一行,相当于C语言中的\n

因为C++的编译器是完成兼容C语言的,所以你可以用"\n"来替换endl

image


cout是流提取操作符,对应的,也有流插入操作符cin。你可以理解为它们就是C++中的printf和scanf

注意这里的箭头方向,不要弄错了

1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;
int main()
{
cout << "Hello, world!" << endl;
int a;
cin >> a;
cout << a << endl;
return 0;
}

image

4.2头文件

使用这两个“库函数”时,必须要要包含iostream头文件以及std标准命名空间

cplusplus.com上搜索,可以看到iostream是标准输入输出流的库

image

4.3和C语言的不同

使用C++来输入输出更加方便,因为我们不需要增加格式控制符%d %c %f这种)

因为编译器会自己判断它们的类型

image

同样的,我们可以使用连续的>>来输入多个数据

image

4.4浮点数默认保留5位小数

通过两个程序的测试,我们可以看到C++中默认保留小数是5位,而C语言中是6位

image

image

那么问题来了,如果我想控制输出格式,让它输出小数点后两位可以怎么做呢?

很简单,直接调用printf%.2f不就完事了?

C++中也有自己的格式控制方式,但是那个比printf更复杂

image

而且在C++中打印低于5位的浮点数,并不会像C语言一样后补0

image

注意注意!如果在C++程序中使用printf,就不能在printf里面写endl啦!


5.命名空间

在输入输出函数中,有这么一个语句

1
using namespace std;

前面已经提到过,std是”标准命名空间”

那什么是命名空间呢?


5.1简单介绍

在main函数中,如果出现了两个相同名字的参数,编译器会报错

image

假设一个班级上有两个同名的同学,为了区分他们俩人,我们会使用一些附加的条件来称呼二者。比如性别、家庭住址、甚至是成绩

C++中的命名空间就是用来干这个事的

项目合作的时候,命名空间很有用,可以有效避免两个人写的程序中出现同样名字的变量而导致冲突的情况

5.2设置一个命名空间

比如现在我们设置一个命名空间,把int a放入其中

1
2
3
namespace muxue {
int a = 1;
}

可以看到,这个时候编译程序就不会报错了

image

但是程序打印的是main函数中的double类型a,如果我们想用muxue中的int a要怎么操作呢?

只需要在变量名之前用::来指定命名空间就可以了

image

当然,我们也可以使用using namespace muxue;来直接展开这个命名空间中的所有东西

但是这样不太好!后面会提到

如果你定义了两个相同名字的命名空间,在编译程序的时候,编译器会合并他们


5.2.1嵌套定义命名空间

定义命名空间就和C语言的结构体很类似,是可以嵌套定义另外一个命名空间的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//命名空间
namespace muxue {
int a = 1;
//嵌套定义命名空间
namespace happy {
int b = 2;
}
}

int main() {
double a = 3.14;
cout << a << endl;
cout << muxue::a <<endl;
cout << muxue::happy::b << endl;
return 0;
}

image

直接展开两个命名空间后,就不需要这样写输出函数了

image


我们还可以在已有命名空间中嵌套定义一个命名空间,里面包含另外一个a

1
2
3
4
5
6
7
namespace muxue {
int a = 1;
//嵌套定义命名空间
namespace happy {
int a = 2;
}
}

这时候如果使用using namespace muxue;,在main函数中会默认打印外层命名空间中的a

image

但如果你比较喜欢作死,把内层命名空间也展开了,就会出现冲突

1
2
using namespace muxue;
using namespace muxue::happy;

image


除了嵌套定义中这种冲突的情况,我们直接展开命名空间时,还会遇到其他冲突

5.3全部展开可能遇到的问题

5.3.1和库函数冲突

比如下面这个情况,我们在muxue里面定义了一个int类型叫做rand,并在之后直接using namespace完整展开了这个命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <stdlib.h>
using namespace std;
namespace muxue {
int a = 1;
int rand = 0;
}
using namespace muxue;
int main(){
a += 1;
cout << a << endl;

rand = 10;
printf("%d\n", rand);
return 0;
}

然后你就会发现,程序boom了!

image

这是因为在<stdlib.h>头文件中有一个生成随机数的rand函数,现在程序不知道你是要用命名空间中的变量rand,还是想调用这个库函数了!

1
2
muxue::rand = 10;
printf("%d\n", muxue::rand);

这时候我们就要选择单独调用命名空间中的rand变量,而不是直接使用变量名

image

同时,顶部的using namesapce也需要做相应修改

1
2
//using namespace muxue;
using muxue::a;

5.3.2和其他命名空间冲突

1
2
3
4
5
6
7
8
9
10
namespace muxue {
int a = 1;
int rand = 0;
}
namespace haha {
int a = 3;
}

using namespace muxue;
using namespace haha;

假设两个命名空间中都有a变量,这时候程序也不知道你要调用哪一个a了,同样会报错

image

这就好比班级里有俩个叫李华的人,本来一个是男的,一个是女的,咱们可以用性别区分

直接完整展开了命名空间,就好比让这两个李华都变成男生

这不BBQ了吗?还怎么用性别判断谁是谁?

所以在很多时候,尽量使用::单独展开某一个变量,而不是展开整个命名空间

这种冲突的情况,被称为命名空间污染


5.4避免命名空间污染

在菜鸟教程上,可以看到有大佬做了一个这样的笔记

image

是的,有的时候我们自定义的变量类型名会和std标准命名空间冲突,这时候直接在最前面using namespace std;就会出5.3中提到的问题

可cout和cin这两个家伙天天要用,总不能每一次使用的时候,都写一个std::吧?那样也太麻烦了!

别急,我们也可以单独展开这几个小家伙!

1
2
3
using std::cin;
using std::cout;
using std::endl;

image

其他一些问题在之前5.25.3都已经提到过了,这里就不再说啦!

个人理解,即便C++有命名空间这个好东西,在定义变量的时候最好还是保持一个良好的编程习惯,不用关键字和库函数名作为变量名

5.5 ::指定全局作用域

当我们默认使用::而不在前面添加命名空间的名字的时候,默认会指定到全局的命名空间。即便函数被另外一个命名空间所包裹,也是会调用全局作用域下的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//全局
void _print()
{
    cout << "global print" << endl;
}
//命名空间
namespace muxue
{
    void _print()
    {
        cout << "namespace muxue print" << endl;
    }

    void testNamespace()
    {
        ::_print();//调用全局作用域的函数
    }
}

int main()
{
    muxue::_print();//调用命名空间内的print
    muxue::testNamespace();

    return 0;
}

打印的结果如下

image


6.缺省参数

缺省参数是函数在定义的时候,给指定的参数一个默认值,如果没有指定实参,就会使用这个默认值。比如:

1
2
3
4
5
6
7
8
9
10
//缺省参数
void Add(int a = 10,int b = 20) {
cout << a + b << endl;
}
int main() {
int n = 1, m = 2;
Add(n, m);//指定了实参
Add();//没有指定
return 0;
}

image

除了这两种方式外,我们还可以只给该函数传一个实参

image

但是像下面这样,想给函数中的b传一个参数是不可以的

image

6.1全缺省和半缺省

  • 像上面这种,函数中的所有形参都设定了缺省值的,叫做全缺省
  • 函数中只有一部分设置了缺省值的,叫做半缺省

注意:设置缺省参数的时候,必须从右往左设置,不能出现中间空一个的情况


6.1.1全缺省

比如这么一个有3个参数的函数,我们全部设置了缺省值,就是全缺省

1
2
3
4
5
void Print(int a = 10, int b = 20,int c=30) {
cout << a << endl;
cout << b << endl;
cout << c << endl;
}

我们可以直接传3个值,进行对应的打印,也可以只传两个、一个或者不传形参

image

这种中间间隔一个传参是不允许的

image


6.1.2半缺省

半缺省是缺省了一部分的函数

1
2
3
4
5
void Print2(int a, int b = 20, int c = 30) {
cout << a << endl;
cout << b << endl;
cout << c << endl;
}

在调用的时候,我们就不能不传参数了

image

至少要传一个参数给我们的a

image

半缺省的函数可以是如下形式

1
2
void Print2(int a, int b = 20, int c = 30);
void Print2(int a, int b, int c = 30);

但不能是这样

1
void Print2(int a=10, int b, int c = 30);

因为这种情况会出现歧义,编译器不知道你的参数是给b还是给a


6.2缺省参数的应用

比如在创建一个顺序表的时候,我们可以利用缺省参数来设置默认长度

这样就避免了单一默认长度导致的多次扩容(realloc扩容是有性能浪费的!)

image

比如我知道我这次需要一个很长的顺序表,直接改默认长度不太合适。使用这种方法传一个新的长度值更加方便我们的动态管理!

缺省参数是只有C++才支持的函数定义方式,这也是C++比C语言更优的体现

6.3多文件编程中的缺省

使用多文件编程的时候,缺省值只需要在头文件中给出即可

image

不需要在函数定义的源文件中给出,编译器会报错

假设函数的声明和定义中设定的缺省值不同,编译器就不知道要用哪一个缺省值

image

调用函数,可以看到函数正确分配了不同的初始值

image


6.3.1错误情况

但如果我们在头文件中不包含缺省,函数就会报错,不支持我们只传1个参数

1
void InitArr(Qa* q, int n);

image

只要你的头文件中声明没有缺省, 在函数定义的源文件中加入缺省也是没用的!

因为编译器展开头文件是在main函数中之上,在查找函数的时候,只会向上查找声明。所以函数的声明是以头文件为准

image


结语

第一篇C++的笔记就写这么多(其实已经有几千字了)

下一篇笔记是函数重载,可以让你感受到C++的更多花样!

不会有人把博客看完了还不点赞吧?

image