C++是一种静态数据类型语言,它的类型检查发生在编译时,编译器检查数据类型是否支持要执行的运算,如果试图执行类型不支持的运算,编译器将报错并且不会生成可执行文件。(与之相对的,如python等动态语言,在程序运行时检查数据类型。)因此,编译器必须知道程序中每一个变量对应的数据类型。
基本内置类型
C++定义了一套包括算术类型和空类型在内的基本数据类型。
算术类型
算术类型分为两类:整型(integral type,包括字符和布尔类型在内)和浮点型。
类型转换
当在程序的某处我们使用了一种类型而其实对象应该取另一种类型时,程序会自动进行类型转换。
当一个算术表达式中既有无符号数又有int值(/有符号类型)时,那个int值(/有符号类型)就会转换成无符号数。
字面值常量
整型和浮点型字面值
字符和字符串字面值
转义序列
布尔字面值和指针字面值
true和false是布尔类型的字面值;nullptr是指针字面值。
变量
变量定义
变量提供一个具名的、可供程序操作的存储空间。C++中的每个变量都有其数据类型,数据类型决定着变量所占内存空间的大小和布局方式、该空间能存储的值得范围,以及变量能参与的运算。
初始化
初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代。
eg. 定义一个名为units_sold的int变量并初始化为0:
列表初始化
用花括号来初始化变量称为列表初始化。
无论是初始化对象还是某些时候为对象赋新值,都可以使用一组由花括号括起来的初始值。
当用于内置类型的变量时,如果我们使用列表初始化且初始值存在丢失信息的风险,则编译器将报错:
默认初始化
如果定义变量时没有指定初值,则变量被默认初始化。
string类规定如果没有指定初值则生成一个空串。
变量声明和定义的关系
为了允许把程序拆分成多个逻辑部分来编写,C++支持分离式编译机制,该机制允许将程序分割为若干个文件,每个文件可被独立编译。
为了支持分离式编译,C++语言将声明和定义区分开来。声明使得名字为程序所知,一个文件如果想使用别处定义的名字则必须包含对那个名字的声明。而定义则负责创建与名字关联的实体。
变量声明规定了变量的类型和名字,这一点和定义相同。但除此之外,定义还申请存储空间,也可能会为变量赋一个初始值。
如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显式地初始化变量:
任何包含了显式初始化的声明即成为定义。我们能给由extern关键字标记的变量赋一个初始值,但是这么做也抵消了extern的作用。extern语句如果包含初始值就不再是声明,而变成定义了:
如果要在多个文件中使用同一个变量,就必须将声明和定义分离。此外,变量的定义必须出现且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。
标识符
C++的标识符由字母、数字、下划线组成,其中必须以字母或下划线开头。
C++为标准库保留了一些名字。用户自定义的标识符中不能连续出现两个下划线,也不能以下划线紧连大写字母开头。此外,定义在函数体外的标识符不能以下划线开头。
变量命名规范
名字的作用域
复合类型
本章介绍引用和指针
引用
引用(reference)为对象起了另外一个名字,引用类型引用(refers to)另外一种类型。通过将声明符写成&d的形式来定义引用类型,其中d是声明的变量名:
无法令引用重新绑定到另外一个对象,因此引用必须初始化。
引用即别名
引用并非对象,相反的,它只是为一个已经存在的对象所起的另外一个名字。
定义了一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的。(引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起。)
因为引用本身不是对象,所以不能定义引用的引用。
允许在一条语句中定义多个引用,其中每个引用标识符都必须以符号&开头。
除了之后要介绍的两种例外情况,所有引用的类型都要和与之绑定的对象严格匹配。
指针
与引用类似,指针也实现了对其他对象的间接访问。然而指针与引用相比又有很多不同点。
①指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。
②指针无须在定义时赋初值。和其他内置类型一样,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值。
定义指针类型的方法将声明符写成*d的形式,其中d是变量名。
空指针
几个生成空指针的方法:
最好使用nullptr,同时尽量避免使用NULL。
建议:初始化所有指针。
void* 指针
void* 是一种特殊的指针类型,可用于存放任意对象的地址。
理解复合类型的声明
从右向左阅读r的定义。&说明r是一个引用,*说明r引用的是一个指针,int指出r引用的是一个int指针。
const限定符
const对象一旦创建后其值就不能再改变,所以const对象必须初始化。
只能在const类型的对象上执行不改变其内容的操作。
默认状态下,const对象仅在文件内有效。当多个文件中出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。
当以编译时初始化的方式定义一个const对象时,编译器将在编译过程中把用到该变量的地方都替换成对应的值。
编译器会找到代码中所有用到bufsize的地方,然后用512替换。
多文件之间共享const对象👆不管是声明还说定义都添加extern关键字。
const的引用
指针和const
要想存放常量对象的地址,只能使用指向常量的指针。
允许令一个指向常量的指针指向一个非常量对象。指向常量的指针仅仅要求不能通过该指针改变对象的值,但那个对象的值可以通过其他途径改变。
const指针
顶层const
顶层const表示指针本身是个常量,底层const表示指针所指的对象是一个常量。
更一般地,顶层const可以表示任意的对象是常量。底层const则与指针和引用等复合类型的基本类型部分有关。
当执行对象的拷贝操作时,拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换。一般来说,非常量可以转换成常量,反之则不行:
constexpr和常量表达式
常量表达式是指值不会改变并且在编译过程中就能得到计算结果的表达式。
允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化:
constexpr把它所定义的对象置为顶层const。
处理类型
类型别名
有两种方法可用于定义类型别名。
①使用关键字typedef
②使用别名声明来定义类型的别名
指针、常量和类型别名
auto类型说明符
auto让编译器通过初始值来推算变量的类型。auto定义的变量必须有初始值。
使用auto也能在一条语句中声明多个变量。因为一条声明语句只能有一个基本数据类型,所以该语句中所有变量的初始基本数据类型都必须一样:
Note:①当引用被用作初始值时,真正参与初始化的其实是引用对象的值。此时编译器以引用对象的类型作为auto的类型:
②auto一般会忽略掉顶层const,同时底层const则会保留下来。
decltype类型指示符
作用:选择并返回操作数的数据类型。在此过程中,编译器分析并得到它的类型,却不实际计算表达式的值。
引用从来都作为其所指对象的同义词出现,只有用在decltype处是一个例外。
如果表达式的内容时解引用操作,则decltype将得到引用类型。
自定义数据结构
类定义的最后要加上分号,类体可以为空。类通常被定义在头文件中,而且类所在有文件的名字应与类的名字一样。
头文件通常包含哪些只能被定义一次的实体,入类、const和constexpr变量等。
预处理器概述
①#inlcude,当预处理器看到#include标记时就会用指定的头文件的内容代替#include。
②