1. 基础知识
高级语言:接近自然语言的编程语言
编译器:将C++源程序代码转换为二进制文件
运行:操作系统读取二进制文件,运行其中的程序
MINGW64
g++
gdb
g++ 01.cpp -o 01
程序文件通常被称为源文件(source file)
C++语言并未定义任何输入输出(IO)语句,包含了一个全面的标准库来提供IO。
iostream
库包含了两个基础类型istream
和ostream
流stream:想表达的是,随着时间的推移,字符是顺序生成或消耗的。
istream类型的对象 cin 标准输入 cout 标准输出
ostream类型的对象 cerr 输出警告和错误消息([图片上传中...(image-20210408111841807.png-745b5b-1617885908812-0)] 标准错误) clog 输出程序运行时的一般信息
系统通常将程序所运行的窗口与这些对象关联起来。因此,当我们读取cin
,数据将从程序正在运行的窗口读入,当我们向cout
、cerr
和clog写入数据时,将会写到同一个窗口。
cout
与cerr
区别1、
cout
经过缓冲后输出,默认情况下是显示器。这是一个被缓冲的输出,是标准输出,并且可以重新定向(关于重新定向的意思还没明白);2、
cerr
不经过缓冲而直接输出,一般用于迅速输出出错信息,是标准错误,默认情况下被关联到标准输出流,但它不被缓冲,也就说错误消息可以直接发送到显示器,而无需等到缓冲区或者新的换行符时,才被显示。一般情况下不被重定向
endl操纵符,写入endl的效果是结束当前行,并将与设备关联的缓冲区buffer中的内容刷新到设备中。
❗缓冲刷新操作可以保证到目前为止,程序所产生的所有输出都真正的写入输出流中,而不是停留再内存中等待写入流。
命名空间可以帮助我们避免不经意的名字定义冲突,以及使用库中相同名字导致的冲突。标准库定义的所有饼子都在命名空间std中。(
std::cout
)作用域运算符
::
注释:通常用于概述算法,确定变量的用途,或者解释难懂的代码段
当我们使用一个
istream
对象作为条件时,其效果是检测流的状态。如果流是有效的,即流未遇到错误,那么检测成功。当遇到文件结束符,或遇到一个无效输入时,istream
对象的状态就会变为无效。处于无效状态的istream
对象会使条件变为假。从键盘输入文件结束符“
Windows
Ctrl+Z
、UNIXCtrl+D
编译: 编译器的一部分工作是寻找程序文本中的错误。编译器没有能力检查一个程序是否按照其作者的意图工作,但可以检查形式上的错误。
语法错误(syntax error)、类型错误(type error)、声明错误(declaration error)
错误常有传递效应
类
一种用于定义自己的数据结构及其相关操作的机制。类时C++中最基本的特性之一。标准库中,如istream和ostream都是类。
一个类定义了一个类型,以及与其关联的一组操作。
成员函数
成员函数是定义未类的一部分的函数,有时也被称为方法。我们通常以一个类对象的名义来调用成员函数。
点运算符只能用于类类型的对象
缓冲区(buffer) 一个存储区域,用于保存数据。IO设施通常将输入(或输出)数据保存在一个缓冲区中,读写缓冲区的动作与程序中的动作是无关的。我们可以显示地刷新缓冲,以便强制将缓冲区的数据写入输出设备。默认情况下,读cin会刷新cout;程序非正常终止时也会刷新cout。
字节: 可寻址的最小内存块
字:存储的基本单位,通常由几个字节组成
大多数计算机将内存中的每个字节与一个地址(address)关联起来
初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新的值来替代。
列表初始化: 作为C++11新标准的一部分,用花括号来初始化变量得到了全面的应用。现在,无论是初始化对象还是某些时候为对象赋新值,都可以使用这样一组由花括号括起来的初始值
这种初始化有一个重要特点:如果我们使用列表初始化且初始值存在丢失信息的风险,则编译器将报错
默认初始化: 如果定义变量时没有指定初值,则变量被默认初始化,此时变量被赋予了"默认值"(由变量类型决定)。
定义于函数体内的内置类型的对象如果没有初始化,则其值未定义。类的对象如果没有显示地初始化,则其值由类确定。
string类规定如果没有指定初值则生成一个空串
为了允许把程序拆成多个逻辑部分来编写,C++语言支持分离式编译机制,该机制允许将程序分割为若干个文件,每个文件可被独立编译。
为了支持分离式编译,C++语言将声明和定义区分开来。声明(declaration)使得名字为程序所知,一个文件如果想使用别处定义的名字必须包含对哪个名字的声明。而定义(definition)负责创建于名字关联的实体。
变量声明 规定了变量的类型和名字,并且还需要申请存储空间,也可能会为变量赋一个初始值
⭐ 如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显示地初始化变量;
extern int i; //声明i而非定义i
int j; //声明并定义了j
任何包含了显示初始化的声明即称为定义。在函数体内部,如果试图初始化一个由extren
关键字标记的变量,将引发错误。
变量能且只能被定义一次,但是可以被多次声明
静态类型
C++是一种静态类型语言,其含义是在编译阶段检查类型。其中检查类型的过程称为类型检查
引用
C++ 11中新增了一种引用: 所谓的"右值引用(rvalue rederence)"
严格来说,当我们使用术语"引用"时,指的时"左值引用"
引用 为对象起了另外一个名字,引用类型引用时另一种类型,通过将声明写成&d的形式来定义引用类型。
int ival = 1024;
int &refVal = ival; // refVal指向ival
int &refVal2; //报错:引用必须被初始化
一般在初始化变量时,初始值会被拷贝到新建的对象中。然而定义引用时,程序把引用和它的初始值绑定(bind)在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。因为无法令引用重新绑定到另外一个对象,因此引用必须初始化。
引用并非对象,相反的,它知识为一个已经存在的对象所起的另外一个名字
任何对引用的操作,其实就是对其所指对象的操作。因为引用本身不是一个对象,所以不能定义引用的引用。
允许一条语句中定义多个引用,其中每个引用标识符都必须以符号&开头。
引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起。
//这样做是可以的哦
int i = 0, &r1 = i;
double d = 0, &r2=d;
r2 = 3.1415; √ r2 = r1; i = r2; r1=d;
指针
指针是指向另外一种类型的符合类型。与引用类似,指针也实现了对其他对象的间接访问。然而指针与引用相比又有许多不同点。其一,指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。其二,指针无须在定义时赋初值。和其它内置类型类型一样,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值。
定义指针: *d
, 其中d是变量名
double dp, *dp2; //dp2是指向double型对象的指针,dp是double型对象
指针存放某个对象的地址,想要获取该地址,需要使用取地址符&
int ival = 42;
int *p = &ival;//p存放变量ival的地址,或者说p是指向ival的指针
第二条语句把p定义为一个指向int的指针,随后初始化p令其指向名为ival的int对象。因为引用不是对象,没有实际地址,所以不能定义指向引用的指针。
double dval;
double *pd = &dval;//正确:初始值是double型对象的地址
double *pd2 = pd; //正确:初始值是指向double 对象的指针
int *pd = pd;//错误:指针pi的类型和pd的类型不匹配
pi = &dval; //错误:试图把double型对象的地址赋给int型指针
因为在声明语句中指针的类型实际上被用于指定它所指对象的类型,所以二者必须匹配。如果指针指向了一个其它类型的对象,对该对象的操作将发生错误。
如果指针指向了一个对象,则允许使用解引用符*
来访问该对象
解引用操作仅适用于那些确实指向了某个对象的有效指针。
空指针
空指针(null pointer)不指向任何对象,在试图使用一个指针之前代码可以首先检查它是否为空。
得到空指针最直接的方法就是用字面值nullptr来初始化指针,这也是C++新标准刚引入的一种方法。nullptr是一种特殊类型的字面值,它可以被转换成任意其它的指针类型。
void* 指针
void*
是一种特殊的指针类型,可用于存放任意对象的地址。一个void*指针存放着一个地址,这一点和其它指针类似。不同的是,我们对该地址中到底是个声明类型的u第项并不了解。
利用void*指针能做的事特别有限:拿它和别的指针比较、作为函数的输入或输出,或者赋给另一个void * 指针。
int ival = 1024;
int *p1 = &ival; //pl指向一个int型的数
int **pp1 = &p1;//pp1指向一个int型的指针
引用本身不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用:
int i = 42;
int *p; //p是一个int型指针
int *&r = p; //r是一个对指针p的引用 int* 指针型
r = &i; //r引用了一个指针,因此给r赋值&i就是令p指向i
*r = 0; //解引用r得到i,也就是p指向的对象,将i的值改为0
const限定符
因为const对象一旦创建后其值就不能再改变,所以const对象必须初始化。
编译器将在编译过程中把用到该const变量的地方都替换成对应的初始值。
默认情况下,const对象被设定为仅在文件内有效。当多个文件中出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。
如果希望const在文件间共享,解决的方法是,对于const变量不管是声明还是定义都添加extern关键字,这样只需定义一次就可以
const引用
对常量的引用":把引用绑定到const对象上,与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象:
const int ci = 1024;
const int &r1 = ci;//正确:引用及其对应的对象都是常量
r1 = 42; //错误:r1是对常量的引用
int &r2 = ci; //错误:试图让一个非常量引用 指向 一个常量对象
- 初始化常量引用时,允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可。尤其允许为一个常量引用绑定非常量变量。
int i = 42;
const int &r1 = i; //√
const int &r2 = 42; //√
const int &r3 = r1*2; //√
int &r4 = r1*2; //×