Chapter1
1.1
- 操作系统通过main来运行C++程序。
- 一个函数的定义包括四个部分:返回类型,函数名,一个括号包围起来的形参列表以及函数体。
- 类型:一种类型不仅定义了数据元素的内容,还定义了这类数据上可以进行的运算。
1.2
- C++提供了一个全面的标准库来提供IO机制。
- iostream库包含了两个基础类型istream和ostream,分别表示输入流和输出流。一个流就是一个字符序列,是从IO设备读出或者写入IO设备的。
- 使用名为cin(标准输入)的istream对象处理输入,使用名为cout(标准输出)的ostream对象处理输出。
- cerr输出警告和错误信息,clog输出程序运行时的一般性信息。
- <<(输出运算符)接受两个运算对象,左侧的运算对象必须是一个ostream对象,右侧的运算对象是要打印的值。此运算符将给定的值写入到给定的ostream中。输出运算符的计算结果就是其左侧运算对象。
- endl(操纵符)写入的效果是结束当前行,并将与设备关联的缓冲区中的内容刷入到设备中、缓冲刷新操作可以保证到目前为止程序所产生的所有输出都真正的写入到了输出流当中,而不是仅仅停留在内存中等待写入流。
- std::前缀指定了名字cout和endl是定义在名为std的命名空间之中的。命名空间可以帮助我们避免不经意的名字定义冲突,标准库中的所有命名均是在std命名空间之中。
- (::)作用域运算符
- >>(输入运算符)接受一个istream对象作为其左侧,接受一个对象作为其右侧运算对象。
1.3 注释简介
- 分为单行注释和界定符对注释
1.4 控制流
- while语句
Chapter2
2.1 基本内置类型
- C++定义了一套包括算术类型和空类型在内的基本数据类型。
- 算术类型:
类型 | 含义 | 最小尺寸(位) |
---|---|---|
bool | 布尔类型 | - |
char | 字符 | 8 |
wchar_t | 宽字符 | 16 |
char16_t | Unicode字符 | 16 |
char32_t | Unicode字符 | 32 |
short | 短整型 | 16 |
int | 整型 | 16 |
long | 长整型 | 32 |
long long | 长整型 | 64 |
float | 单精度浮点数 | 6位有效数字 |
double | 双精度浮点数 | 10位有效数字 |
long double | 扩展精度浮点数 | 10位浮点数 |
- 布尔类型的取值为true或者false
- Unicode是用于表示所有自然语言中字符的标准
- 除去布尔型和扩展的字符型之外,其它整型可以划分为带符号的和无符号的两种。类型int ,short ,long ,long long 都是带符号的,通常在这些类型名前添加
unsigned
就可以得到无符号类型了
字符型被分为三种:char,signed char,unsigned char。类型char和类型 signed char 并不一样。尽管char有三种类型,但是字符的表现形式却只有两种:有符号的和无符号的
- 对象的类型确定了对象所能包含的数据和对象能够参与的运算,其中有一种运算被大多数类型所接受,就是从一种类型转化为另一种相关的类型。
类型转换:
- 当我们把一个非布尔类型的算术值赋给布尔类型时,初始值为0则结果为false,其它结果为true。
- 当我们把一个布尔值赋值给一个非布尔类型时,初始值为false则结果为0,初始值为true则结果为1。
- 当我们把一个浮点数赋值给一个整型时,将只保留小数点前面的部分。
- 当我们把一个整型赋值给浮点型时,小数部分为0,如果该整型所占空间超出了浮点类型的容量,精度可能会有损失。
- 当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。
- 当我们赋给一个带符号类型一个超出它表示范围的额值时,结果是未可知的
当一个算术表达式中既有有符号值又有无符号值时,那么有符号值将会自动被转化为无符号值。结果也将是一个无符号值类型,所以一个无符号数减去另一个数时,我们必须保证结果不能是一个负数。
int main() {
int a=-34;
unsigned b=12;
std::cout<<a+b<<std::endl;//4294967274
return 0;
}
*无符号数永远不会小于0,尤其是在写循环条件的时候要注意。
6. 字面值常量:我们可以将整型字面值写成十进制,八进制(0开头)或者十六进制(0x开头),整型字面值的具体类型由它的值和符号决定。默认情况下,十进制的值是带符号的,八进制和十六进制可以带符号也可以不带符号。十进制字面值是int,long,long long中最小的能够容下它的类型。
7. 严格来说,十进制字面值不会取负数(负号并不在字面值之内,它的作用仅仅是对字面值取负)。
8.
类型 | 字面值常量 |
---|---|
浮点型 | 字面值表现为一个小数或以科学计数法表示的指数,默认的浮点型字面值类型是double。 |
char型 | 字面值是由是由单引号括起来的一个字符。‘’ |
字符串型 | 字面值实际上是由常量字符构成的数组。编译器在每一个字符串的结尾处添加一个空字符(‘\0’)因此字符串字面值的实际长度要比它的内容多1。 |
布尔型 | true或者false |
指针 | nullptr |
std::cout<<"a really really long string literal"
"that spans two lines"<<std::endl;
- 通过给字面值添加前缀或者后缀可以改变它的默认类型:
前缀 | 含义 | 类型 |
---|---|---|
u | Unicode16字符 | char16_t |
U | Unicode32字符 | char32_t |
L | 宽字符 | wchar_t |
u8 | UTF-8(仅用于字符串字面值常量) | char |
后缀 | 含义 | 类型 |
---|---|---|
整型字面值 | ||
u or U | unsigned | |
l or L | long | |
ll or LL | long long | |
浮点型字面值 | ||
f or F | float | |
l or L | long double |
2.2 变量
- 变量定义:
int sum=0,value;
Class_type item;
std::string s;//定义字符串类型
- 对象:对象是指一块能够存储数据并且具有某种类型的内存空间。
- 在C++中赋值和初始化是两种完全不同的操作。
- 在C++11标准中,使用花括号来初始化得到了全面的应用,这种初始化的形式被称为列表初始化。当用于内置类型的变量时,如果我们使用列表初始化且初始化的值存在丢失信息的风险,则编译器会报错。
- 初始化不是赋值,初始化的含义是在创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来代替。
- 如果定义变量时没有被指定初值,则变量将会被默认初始化。定义在任何函数体之外的内置类型变量会被默认初始化为0,定义在函数体内部的变量不会被初始化。
- 为了允许吧程序拆分为多个部分来编写,C++语言支持分离式编译机制,该机制允许将程序分割为若干个文件,每个文件可以被独立编译。
- 为了支持分离式编译,C++语言将声明和定义区分开来。声明使得名字为程序所知,一个文件如果想要使用别处别处定义的名字,则必须包含对那个名字的声明。而定义负责创建与名字关联的实体。声明明确规定了变量的类型和名字,这点与定义相同,但是定义还申请存储空间,也可能会为变量赋一个初始值。
- 如果要声明一个变量而非定义它,要在前面添加一个关键字
extern
,在函数体内部,如果试图初始化一个由extern标记的变量,将会引发错误。
extern int i=0;//声明一个变量
int j;//声明并定义一个变量
- 变量能且只能被定义一次,但可以被多次声明。
- C++是一种静态类型的语言,其含义是在编译的时候进行类型检查
- C++的标识符有字母数字和下划线组成,其中首字符必须是字母或者下划线,命名规范与java同。
- 无论在程序的什么位置,使用到的每一个名字都会指向一个特定的实体:变量、函数、类型等等。然而,同一个名字如果出现在程序的不同位置,也可能指向的是不同的实体。
- 作用域是程序的一部分,在其中名字有其特定的含义,C++语言中有大多数作用域都以花括号分隔。同一个名字在不同的作用域中可能指向不同的实体。名字的有效区域开始于名字的声明语句,以声明语句所在的作用域末端为结束
- 定义在函数体之外的名字具有全局作用域。一旦声明后,全局作用域内的所有名字在整个程序的范围内都是可用的。,定义在函数内部的名字拥有块作用域。
- 一般来说,在一个变量被使用的地方附近定义它是一个好的选择,因为这样做能够更容易的找到变量的定义,更重要的是,当变量的定义距离它第一次被使用很近的时候,我们也会赋给它一个比较合理的初始值。
- 作用域能够彼此包含,被包含的作用域称为内层作用域,包含着别的作用域的作用域称为外层作用域。作用域一旦声明了某个名字,它所嵌套着的所有的作用域都将能够访问该名字。同时,允许在内层作用域中重新定义外层作用域中已经有的名字。
2.3 复合类型
- 复合类型是基于其它类型定义的类型。
- 一条声明语句是由一个基本数据类型和紧随其后的一个声明符列表组成的。
- 引用为对象另外取了一个名字,引用类型引用另外一种类型。
int val=1234;
int &refVal=val;//refVal指向val(是val的另外一个名字)
int &refVal2;//报错,引用必须要初始化
- 程序把引用和初始值绑定在一起而不是将初始值拷贝给引用。一旦初始化完成,引用将合它的初始值一直绑定在一起。因为无法令引用重新绑定一个新的初始值,所以必须对引用进行初始化。
- 引用并非一个对象,相反的,它只是为一个已经存在的对象起的另外一个名字。
- 指针:指针是“指向”另一种类型的符合类型。与引用类似,指针也实现了对其它对象的间接访问。指针与引用也有很多不同的地方:指针本身就是一个对象,允许对指针进行赋值和拷贝,而且在指针的生命周期内它可以先后指向不同的对象。第二,指针无需在定义的时候赋初始值。和其它内置类型一样,在块作用域定义的指针如果没有定义初始值,也将被赋予一个不确定的值。
- 定义指针类型的方法将声明符写成*d的形式,其中d是变量名。如果在一条语句中定义了几个指针类型的变量,每个变量的前面都必须有符号*.
- 指针存放某个对象的地址,如果想要获得该地址,需要使用取址符(操作符&):
- 指针的值应该属于以下四种状态的一种:
- 指向一个对象;
- 指向紧邻对象所占空间的下一个位置;
- 空指针,意味着指针没有指向任何对象;
- 无效指针,也就是上述情况之外的情况。
- 试图拷贝或者以其他方式访问无效指针的值都将引发错误。编辑器并不负责检查此类错误
- 如果指针指向了某一个对象,则必须使用解引用符(*)来访问该对象
- 指向一个对象;
int a=42;
int *p=&a;
std::cout<<p<<std::endl;//输出对象a的地址
std::cout<<*p<<std::endl;//输出对象a的值
- &与*符号的上下文决定了符号的意义
符号 | 上下文 | 含义 |
---|---|---|
& | int &r=i; | 作为声明的一部分,表示r是一个引用 |
* | int *p | 作为声明的一部分,表示p是一个指针 |
& | p=&i | &出现在表达式中,是一个取地址符 |
* | *p=i | 出现在表达式中,是一个解引用符 |
13. 空指针不指向任何对象,在试图使用任何一个指针之前代码可以首先检查指针是否为空,下面是生成空指针的几种方法:
int *p1=nullptr;
int *p2=0;
int *p3=NULL;
- 声明指针的方式:
int* a,b;//a是int型指针,b是int型变量
int *a,*b;//a是int型指针,b是int型变量
2. 4 const 限定符
- const关键字可以保证变量不被改变。即定义常量的关键字。
- 因为const对象一旦被创建之后就无法再改变,所以必须对其进行初始化。
- const对象仅再文件内有效。当多个文件中出现了同名的const对象的时候,其相当于在每一个文件中单独定义了一个独立的const对象。
- 编译器在编译的过程当中会将每一个使用到const对象的地方替换为其常量值。
- 为了能够在不同的文件之间共享const对象,可以使用extern关键字,这样只需定义一次,就可以在不同的文件之间共享常量。
- 对常量对象的引用必须是常量引用。
const int a=2;
int &ra=a;//错误
const int &ra=a;//正确
- 常量引用不能用来修改她所引用的对象。
int a=3;
int &ra=a;
ra=4;//正确,a的值被修改为4
const int &cra=a;
cra=5;//错误,常量引用不能被用来修改引用对象的值
- 允许为一个常量引用绑定非常量的对象,字面值甚至是一个一般表达式。
int a=3;
int &ra=42;//错误
int &ra=a*5;//错误
int ra=4;//正确,a的值被修改为4
const int &cra0=a;//正确
const int &cra1=43;//正确
const int &cra2=a*2;//正确
- 在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的值能转化为引用的类型即可。
double dval=3.14;
const int &ri=dval;//正确
在上面对引用进行初始化的时候,编译器将会执行如下操作:在这种情况下,引用实际上是绑定了一个临时量对象。
double dval=3.14;
const int temp=dval;
const int &ri=temp;//正确