2.1 基本内置类型
2.1.1 算术类型
- 一个char的空间应确保可以存放机器基本字符集中任意字符对应的数字值 => 一个char的大小和一个机器字节一样
- char16_t、char32_t为Unicode字符集服务
- long long C++11 新定义的
- 一般来说,类型float和double分别有7和16个有效位
- unsigned int 可以缩写为 unsigned
- 类型char 会表现为 unsigned char 和 signed char中的一种,具体哪种由编译器决定
- 类型选择的经验准则:
- 当明确知晓数值不可能为负时,选用无符号类型
- 使用int执行整数运算,如果超过了int范围,选用long long
- 如果需要使用一个不大的整数,可以明确指定它的类型是signed char 或 unsigned char
- 执行浮点数运算首选double
2.1.2 类型转换
- 当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数
- 当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的
2.1.3 字面值常量
-
整型和浮点型字面值
- 默认情况下,十进制字面值是带符号数 ——> int long longlong 中尺寸最小的
- 八进制和十六进制字面值既可能是带符号的,也可能是无符号的 ——> int、unsigned int、long、unsigned long、longlong、unsigned longlong中的尺寸最小者
- 如果一个字面值连与之关联的最大的数据类型都放不下,将产生错误
- 类型short没有对应的字面值
- 如果我们使用一个形如-42的负十进制字面值,那个负号并不在字面值之内,作用仅仅是对字面值取负值而已
- 默认,浮点型字面值是一个double
-
字符和字符串字面值
- 如果两个字符串字面值位置紧邻且仅由空格、缩进和换行符分隔,则它们实际上是一个整体
-
转义序列
- 泛化的转义序列:
- \x后紧跟1个或多个十六进制数字 要用到后面所有的数字
- \后紧跟1个、2个或3个八进制数字 如果超过三个,只有前3个数字与\构成转义序列
- 泛化的转义序列:
2.2 变量
2.2.1 变量定义
-
对象:一块能存储数据并具有某种类型的内存空间
-
在同一条定义语句中,可以用先定义的变量值去初始化后定义的其他变量
-
在C++中,初始化和复制是两个完全不同的操作
- 初始化的含义是创建变量时赋予其一个初始值
- 赋值的含义是把对象的当前值擦除,以一个新值来代替
-
列表初始化
- 当用于内置类型的变量时,如果使用列表初始化且初始值存在丢失信息的风险,则编译器将报错
-
默认初始化
- 定义变量时没有指定初值,就会被默认初始化,被赋予了默认值
- 如果是内置类型的变量,它的值由定义的位置决定
- 定义于任何函数体之外的变量被初始化为0
- 定义在函数体内部的内置类型变量将不被初始化
- 如果是内置类型的变量,它的值由定义的位置决定
- 定义变量时没有指定初值,就会被默认初始化,被赋予了默认值
2.2.2 变量声明和定义的关系
-
C++ 支持分离式编译
-
声明使得名字为程序所知,定义负责创建与名字关联的实体
-
声明一个变量而不定义它,需要用extern关键字,而且不要显式初始化变量
- 在函数体内部如果试图初始化一个用extern关键字标记的变量,将引发错误
-
C++是静态类型语言,在编译阶段检查类型(类型检查)
2.2.3 标识符
-
标识符对大小写字母是敏感的
-
不能连续出现两个下划线,也不能以下划线紧连大写字母开头
-
定义在函数体外的标识符不能以下划线开头
-
一些规范:
- 变量名一般用小写字母
- 自定义的类一般以大写字母开头
- 由多个单词组成时,单词之间应有明显区分
-
C++操作符替代名
2.2.4 名字的作用域
- 名字的有效区域始于名字的声明语句,以声明语句所在的作用域末端为结束
- 全局作用域:一旦声明之后,在整个程序的范围内都可使用
- 块作用域
- 作用域中一旦声明了某个名字,它所嵌套着的所有作用域中都能访问该名字。同时,允许在内层作用域中重新定义外层作用域已有的名字
- 全局作用域本身并没有名字,所以当作用域操作符的左侧为空时,向全局作用域发出请求获取作用域操作符右侧名字对应的变量
2.3 复合类型
- 一条声明语句由一个基本数据类型和紧随其后的一个声明符列表组成
2.3.1 引用
- 为对象另外起了一个名字
- 引用必须被初始化
- 定义引用时,程序把引用和它的初始值绑定在一起
- 无法令引用重新绑定到另外一个对象
- 引用并非对象,它只是为一个已经存在的对象所起的另外一个名字
- 因为引用本身不是一个对象,所以不能定义引用的引用
- 所有引用的类型都要和与之绑定的对象严格匹配(存在例外情况)
- 引用只能绑定在对象上,不能与字面值或某个表达式的计算结果绑定在一起
2.3.2 指针
-
指针本身就是一个对象,支持对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象
-
指针无需在定义时赋初值
-
不能定义指向引用的指针
-
所有指针的类型都要和与之绑定的对象严格匹配(存在例外情况)
-
当用到一个预处理变量时,预处理器会自动将它替换成实际值
-
使用非法指针作为条件或进行比较都会引发不可预计的后果
-
void* 指针
- 可用于存放任意对象的地址
- 不能直接操作void* 指针所指的对象 因为我们并不知道这个对象到底是什么类型,也就无法确定能在这个对象上做哪些操作
2.3.3 理解复合类型的声明
- 离变量名最近的符号对变量的类型有最直接的影响 (从右向左阅读)
2.4 const限定符
- const对象必须初始化
- 只能在const对象上执行不改变其内容的操作
- 如果利用一个对象去初始化另外一个对象,则它们是不是const都无关紧要
- 常量特征仅仅在执行改变其值的操作时才会发挥作用
- 默认情况下,const对象被设定为仅在文件中有效,当多个文件中出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量
- 如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern 关键字
2.4.1 const的引用
- 对常量的引用不能被用作修改它所绑定的对象
- 引用的对象是常量还是非常量可以决定其所能参与的操作,却无论如何都不会影响到引用和对象的绑定关系本身
- 在初始化常量引用时,允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可
- 允许为一个常量引用绑定非常量的对象、字面值,甚至是个一般表达式
- 不可以通过对const的引用来修改值
2.4.2 指针和const
指向常量的指针
- 指向常量的指针,不能用于改变其所指对象值(类似对const的引用)
- 要想存放常量对象的地址,只能使用指向常量的指针
- 允许另一个指向常量的指针指向一个非常量对象
- 所谓指向常量的指针,仅仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其他途径改变
const指针
- 常量指针必须初始化(常量嘛 很好理解)
2.4.3 顶层const
- 顶层const => 指针本身是个常量
- 底层const => 指针所指的对象是个常量
- 对常量对象取地址是一种底层const
- 更一般化,顶层const可以表示任意的对象是常量
- 当执行对象的拷贝操作时,常量是顶层const还是底层const区别明显
- 顶层const不受什么影响
- 底层const则要求拷入拷出的对象必须具有相同的底层const资格 或者两个对象的数据类型必须能够互相转换(一般来说非常量可以转换成常量)
2.4.4 constexpr 和 常量表达式
- 值不会改变,并且在编译过程就能得到计算结果的表达式
- 字面值属于常量表达式
- 用常量表达式初始化的const对象也是常量表达式
- constexpr变量:
- 声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化
- 新标准允许定义一种特殊的constexpr函数,这种函数应该足够简单以使得编译时就可以计算其结果
- 可以用constexpr函数去初始化constexpr变量
- 只有字面值类型可以被声明称constexpr类型:算术类型、引用、指针等(不包括string、自定义类、IO库)
- 一个constexpr指针的初始值必须是nullptr、0,或是存储于某个固定地址中的对象
- 函数体内定义的变量一般来说并非放在固定地址中,因此constexpr指针不能指向这样的变量
- 限定符constexpr仅对指针有效,与指针所指对象无关: constexpr int * q = nullptr => q是一个指向整型的常量指针
- constexpr把它所定义的对象置为了顶层const
2.5 处理类型
2.5.1 类型别名
2.5.2 auto类型说明符
- auto 定义的变量必须有初始值
- auto 也能在一条语句中声明多个变量,该语句中所有变量的初始基本数据类型都必须一样
- 当引用被用作初始值时,真正参与初始化的其实是引用对象的值
- auto一般会忽略掉顶层const,同时底层const都会保留下来
- 如果希望推断出的auto类型是一个顶层const,需要明确指出
- 设置一个类型为auto的引用时,初始值中的顶层常量属性仍然保留
- 要在一条语句中定义多个变量,初始值必须是同一种类型
2.5.3 decltype类型指示符
-
选择并返回操作数的数据类型
- 编译器分析表达式并得到它的类型,但不实际计算表达式的值
-
处理顶层const
- decltype会返回该变量的类型,包括顶层const和引用在内
-
decltype中用引用类型,结果也会是引用类型,如果想获得引用所指的类型,可以把引用作为表达式的一部分
-
如果表达式内容是解引用操作,则decltype将得到引用类型 => 解引用指针可以得到指针所指的对象,还能给这个对象赋值
-
如果decltype使用的是一个不加括号的变量,则得到的结果就是该变量的类型;如果给变量加上了一层或多层括号,编译器就会把它当成是一个表达式,变量是一种可以作为赋值语句左值的特殊表达式,所以这样的decltype就会得到引用类型
2.6 自定义数据结构
2.6.1 定义Sales_data类型
- 可以为数据成员提供一个类内初始值,没有类内初始值的成员将被默认初始化
2.6.2 使用Sales_data类
2.6.3 编写自己的头文件
- 头文件通常包含那些只能被定义一次的实体,如类、const和constexpr变量等
- 头文件一旦改变,相关的源文件必须重新编译以获取更新过的声明
- 预处理器:
- 确保头文件多次包含仍能安全工作
- 在编译之前执行的一段程序
- 头文件保护符 依赖于预处理变量
- #define 把一个名字设定为预处理变量
- #ifdef 当且仅当变量已定义时为真
- #ifndef 当且仅当变量未定义是为真
- #endif 一旦检查结果为真,则执行后续操作直到遇到#endif为止
- 预处理变量无视C++语言中关于作用域的规则 => 整个程序中预处理变量包括头文件保护符必须唯一