C++入门基础篇(一)
前言:有过C语言学习的基础,C++入门很简单的,将用两篇来写完C++入门基础。
一.C++的第一个程序
C++兼容C语言绝大多数的语法,所以C语言实现的hello world依旧可以运行,C++中需要把定义文件代码后缀改为.cpp,vs编译器看到是.cpp就会调用C++编译器编译,linux下要用g++编译,不再是gcc。
当然C++有一套自己的输入输出,严格说C++版本的hello world应该是这样写的:
int main()
{cout << "hello world\n" << endl;return 0;
}
二.命名空间
2.1 namespace的价值
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
c语言项目类似下面程序这样的命名冲突是普遍存在的问题,C++引入namespace就是为了更好的解决这样的问题。
我们来看一个问题:
该程序如果我们不加stdlib.h头文件,不会报错,但我们加上的话就会报错。
2.2 namespace的定义
•定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对}即可,}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
•namespace本质是定义出一个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand不在冲突了。
•C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找一个变量/函数/类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量生命周期。
•namespace只能定义在全局,当然他还可以嵌套定义。
•项目工程中多文件中定义的同名namespace会认为是一个namespace,不会冲突。
•C++标准库都放在一个叫std(standard)的命名空间中。
下面都是对概念用相关例子来说明。
来解决我们上面代码出现的报错。
就算这时候库里面也有Add,rand和Node,也不会发生冲突,本质他们不是在同一个域里面的。
一般函数里面定义的那些叫局部域,还有我们写一个循环里面定义的又是一个更小的局部域。函数外面的都叫全局域,变量查找在域中默认的规则:没有指定的情况下,先在局部域查找,再在全局域查找。
比如:
当用到rand的时候,编译器去查找,先在局部域查找,没有,然后再去全局域查找,但全局有两个rand,一个是变量,一个是函数,所以发生冲突。
当我们把rand用命名空间封装起来,还是同样的查找规则,最后在头文件stdlib.h中找到,编译链接的阶段会预处理,将头文件的内容展开,打印的结果应该是函数地址。
查找默认规则:先在局部域查找,再到全局域查找,不会到命名空间域查找。
如果我们想要用命名空间里面的变量该怎么用呢?
2.3 命名空间使用
编译查找一个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以下面程序会编译报错。所以我们要使用命名空间中定义的变量/函数,有三种方式:
· 指定命名空间访问,项目中推荐这种方式。
· using将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式。
· 展开命名空间中全部成员,项目不推荐,冲突风险很大,日常小练习程序为了方便推荐使用。
::这是一个新的运算符,这个运算符叫做域作用限定符。这个时候我们指定了,所以直接会去该命名空间域中去查找。
嵌套定义命名空间的使⽤
使用结构体,因为命名空间封装的是命名的名字,所以我们应该这样写:
还有一点是:多个文件使用同一个命名空间来封装,都会合并在一起,但是要保证如果栈和队列文件合并一起的函数的命名不会发生冲突。
三.C++输入&输出
• 是 Input Output Stream的缩写,是标准的输入、输出流库,定义了标准的输入、输出对象。
•std::cin 是 istream 类的对象,它主要面向窄字符(narrow characters (of type char))的标准输入流。
•std::cout是ostream类的对象,它主要面向窄字符的标准输出流。
•std::endl 是一个函数,流插入输出时,相当于插入一个换行字符加刷新缓冲区。
•<是流插入运算符,>>是流提取运算符。(C语言还用这两个运算符做位运算左移/右移)使用C++输入输更方便,不需要像printf/scanf输入输出时那样,需要手动指定格式,C++的输入输出可以自动识别变量类型(本质是通过函数重载实现的,这个以后会讲到),其实最重要的是C++的流能更好的支持自定义类型对象的输入输出。
• IO流涉及类和对象,运算符重载、继承等很多面向对象的知识,这些知识我们还没有讲解,所以这里我们只能简单认识一下C++IO流的用法,后面我们会有专门的一个章节来细节IO流库。
•cout/cin/endl等都属于C++标准库,C++标准库都放在一个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用他们。
•cout/cin/endl等都属于C++标准库,C++标准库都放在一个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用他们。
•一般日常练习中我们可以using namespace std,实际项目开发中不建议using namespace std。· 这里我们没有包含<stdio.h>,也可以使用printf和scanf,在包含<iostream>间接包含了。vs系列编译器是这样的,其他编译器可能会报错。
如果打竞赛用c++写,极端情况下,需要大量的输入输出过不去的话,加上以下三段代码:
ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
本质:关掉c++和c同步问题,因为c++兼容c,提高效率。
四.缺省参数
•缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省参数。(有些地方把缺省参数也叫默认参数)
•全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。
•带缺省参数的函数调用,C++规定必须从左到右依次给实参,不能跳跃给实参。
•函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值。
// Stack.h#include <iostream>#include <assert.h>using namespace std;typedef int STDataType;typedef struct Stack{STDataType* a;int top;int capacity;
}ST;void STInit(ST* ps, int n = 4);// Stack.cpp#include"Stack.h"// 缺省参数不能声明和定义同时给 void STInit(ST* ps, int n)
{assert(ps && n > 0);ps->a = (STDataType*)malloc(n * sizeof(STDataType));ps->top = 0;ps->capacity = n;
}// test.cpp#include"Stack.h"int main()
{ST s1;STInit(&s1);// 确定知道要插⼊1000个数据,初始化时⼀把开好,避免扩容 ST s2;STInit(&s2, 1000);return 0;
}
当我们用栈插入的时候,如果知道了需要用多少空间,可以通过用缺省参数,来减少频繁扩容,插入时没有扩容,提高效率,如果不知道插入多少数据,给一个适中的缺省值,不够继续扩。