类型分为非常量和常量,一个常量对象必须初始化,而且一旦初始化其值就不能再改变。此外,还可以定义复合类型,如指针和引用等。 复合类型的定义以其他类型为基础。
C++语言允许用户以类的形式自定义类型。C++库通过类提供了一套高级抽象类型,如输入输出和string等。
&运算符(&operator)取地址运算符。
临时值(temporary)编译器在计算表达式结果时创建的无名对象。为某表达式创建了一个临时值,则此临时值将一直存在直到包含有该表达式的最大的表达式计算完成为止。
2.1 基本内置类型
2.1.1 算术类型
(1)地址(address)是一个数字,根据它可以找到内存中的一个字节。
(2)算术类型(array)是一种数据结构,存放着一组未命名的对象,可以通过索引来访问这些对象。
(3)字节(byte)内存中可寻址的最小单元,大多数机器的字节占8位。
(4)字(word)在指定机器上进行整数运算的自然单位。一般来说,字的空间足够存放地址,32位机器上的字通常占据4个字节。
带符号类型(signed)保存正数、负数或0的整型。
— int、short、long 和 long long 都是 带符号, 在前面加unsigned 可变成无符号的。
— char, signed char, unsigned char , char有没有符号看编译器。
无符号类型(unsigned)保存大于等于0的整型。
当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。
当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的。此时,程序可能继续工作、可能崩溃,也可能生成垃圾数据。
2.1.2 类型转换
(1)转换(conversion)一种类型的值转变成另外一种类型值的过程。C++语言支持内置类型之间的转换。
— 非布尔类型->布尔类型 0 -> false, other -> true
— 布尔类型->非布尔类型 false -> 0, true -> 1
— 浮点<->整数,如果整数所占的空间超过浮点类型的容量,精度可能有损失。->
// 错误:变量u永远也不会小于0,循环条件一直成立
1 | for (unsigned u = 10; u >= 0; --u) |
当u==-1时,-1被自动转换成一个无符号数,假设int占32位,则u=4294967295
(2)类型检查(type checking)是一个过程,编译器检查程序使用某给定类型对象的方式与该类型的定义是否一致。
(3)void类型 是一种有特殊用处的类型,既无操作也无值。不能定义一个void类型的变量。
2.1.3 字面值常量
字面值(literal)是一个不能改变的值,如数字、字符、字符串等。单引号内的是字符字面值,双引号内的是字符串字面值。
转义序列(escape sequence)字符特别是那些不可打印字符的替代形式。转义以反斜线开头,后面紧跟一个字符,或者不多余3个八进制数字,或者字母x加上1个十六进制数。
换行符 \n | 横向制表符 \t | 报警符 \a |
---|---|---|
纵向制表符 \v | 退格符 \b | 双引号 \” |
反斜线 \\ | 问号\? | 单引号 \’ |
回车符 \r | 进纸符 \f |
类型说明符(type specifier)类型的名字。
基本类型(base type)是类型说明符,可用const修饰,在声明语句中位于声明符之前。基本类型提供了最常见的数据类型,以此为基础构建声明符。
2.2 变量
变量(variable)命名的对象或引用。C++语言要求变量要先声明后使用。
字符串(string)是一种库类型,表示可变长字符序列。
2.2.1 变量定义
(1)被初始化(initialized)变量在定义的同时被赋予初始值,变量一般都应该被初始化。
— int units_sold = 0;
— int units_sold = {0}; // 列表初始化
— int units_sold{0}; // 列表初始化
— int units_sold(0);
(2)列表初始化(list initialization)利用花括号把一个或多个初始值放在一起的初始化形式。
使用列表初始化且初始值存在丢失信息的风险,则编译器将报错:
— long double ld = 3.1415;
— int a{ld}, b = {ld} // wrong
— int c(ld), d = ld; // right
(3)默认初始化(default initialization)当对象未被显式地赋予初始值时执行的初始化行为。由类本身负责执行的类对象的初始化行为。全局作用域的内置类型对象初始化为0;局部作用域的对象未被初始化即拥有未定义的值。
(4)未初始化(uninitialized)变量已定义但未被赋予初始值。一般来说,试图访问未初始化变量的值将引发未定义行为。
(5)未定义(undefined)即C++语言没有明确规定的情况。不论是否有意为之,未定义行为都可能引发难以追踪的运行时错误、安全问题和可移植性问题。
2.2.2 变量声明和定义
(1)分离式编译(separate compilation)把程序分割为多个单独文件的能力。
(2)声明(declaration)声称存在一个变量、函数或是别处定义的类型。名字必须在定义或声明之后才能使用。
(3)声明符(declarator)是声明的一部分,包括被定义的名字和类型修饰符,其中类型修饰符可以有也可以没有。
(4)定义(definition)为某一特定类型的变量申请存储空间,可以选择初始化该变量。名字必须在定义或声明之后才能使用。
如果想声明变量而非定义,用关键字extern
1 | extern int i; // 声明i |
变量只能被定义一次,但是可以被多次声明
2.2.3 标识符
(1)标识符(identifier)组成名字的字符序列,标识符对大小写敏感。
变量命名有许多约定俗成的规范,下面的这些规范能有效提高程序的可读性:
- 标识符要能体现实际含义
- 变量名一般用小写字母,如index,不要使用Index或INDEX
- 用户自定义的类名一般以大写字母开头,如Sales_item
- 如果标识符由多个单词组成,则单词见应有明显区分,如student_loan 或 studentLoan,不要使用studentloan
2.2.4 名字的作用域
(1)全局作用域(global scope)位于其他所有作用域之外的作用域。
(2)在作用域内(in scope)名字在当前作用域内可见。
(3)内层作用域(inner scope)嵌套在其他作用域之内的作用域。
(4)局部作用域(local scope)是块作用域的习惯叫法。
(5)外层作用域(outer scope)嵌套着别的作用域的作用域。
(6)作用域(scope)是程序的一部分,在其中某些名字有意义。C++有几级作用域:
全局(global)——名字定义在所有其他作用域之外。
类(class)——名字定义在类内部。
命名空间(namespace)——名字定义在命名空间内部。
块(block)——名字定义在快内部。
名字从声明位置开始直至声明语句所在的作用域末端为止都是可用的。
2.3 复合类型
2.3.1 引用
(1)引用(reference)是某个对象的别名。
— int ival = 1024;
— int &refVal = oval;
— int &refVal2; // wrong 引用必须被初始化
(2)绑定(bind)令某个名字于给定的实体关联在一起,使用该名字也就是使用该实体。例如,引用就是将某个名字于某个对象绑定在一起。
2.3.2 指针
(1)指针(pointer)是一个对象,存放着某个对象的地址,或者某个对象存储区域之后的下一地址,或者0。
指针存放某个对象的地址,
— int oval = 42;
— int p = &ival;
指针值:
指针的值(即地址)应该 指向一个对象 / 指向紧邻对象所占空间的下一个位置 / 空指针,没有指向任何对象 / 无效指针
(2)运算符(operator)解引用运算符。解引用运算符。解引用一个指针将返回该指针所指的对象,为解引用的结果赋值也就是为指针所指的对象赋值。
(3)空指针(null pointer)值为0的指针,空指针合法但是不指向任何对象。
(4)nullptr 是表示空指针的字面值常量。
只要指针拥有一个合法值,且非0,则可以用于条件判断。
(5)void 可以指向任意非常量的指针类型,不能执行解引用操作。
2.3.3 理解复合类型的声明
复合类型(compound type)是一种类型,它的定义以其他类型为基础。
1 | //指向指针的指针 |
2.4 限定符
const是一种类型修饰符,用于说明永不改变的对象。const对象一旦定义就无法再赋新值,所以必须初始化。
默认状态下,const对象仅在文件内有效。
某些时有这样一种const变量,它的初始值不是一个常量表达式,但又确实有必要在文件间共享。
解决办法是不管是声明还是定义都加extern关键字
1 | // file_1.cc 定义并初始化了一个常量,该常量能被其他文件访问 |
2.4.1 const的引用
(1)常量引用(const reference)是一种习惯叫法,含义是指向常量的引用。
对常量的引用不能修改所绑定的对象。
对const的引用可能引用一个并非const的对象。
(2)对常量的引用(reference to const)是一个引用,不能用来改变它所绑定对象的值。对常量的引用可以绑定常量对象,或者非常量对象,或者表达式的结果。
2.4.2 指针和const
(1)常量指针(const pointer)是一种指针,它的值永远不变。
(2)指向常量的指针(pointer to const)是一个指针,存放着某个常量对象的地址。指向常量的指针不能用来改变它所指对象的值。
2.4.3 顶层const
(1)顶层const(top-level const)是一个const,规定某对象的值不能改变。
(2)底层const(low-level const)一个不属于顶层的const,类型如果由底层常量定义,则不能被忽略。
顶层const : 指针本身是常量
底层const:指针指向的对象是个常量
2.4.4 constexpr和常量表达式
(1)常量表达式(const expression)能在编译时计算并获取结果的表达式,值不会改变。
(2)constexpr是一种函数,用于代表一条常量表达式。
2.5 处理类型
2.5.1 类型别名
(1)类型别名(type alias)是一个名字,是另外一个类型的同义词,通过关键字typedef或别名声明语句来定义。
(2)typedef作为声明的基本类型出现时,声明中定义的名字就是类型名。
(3)别名(alias declaration)为另外一种类型定义一个同义词:使用“名字=类型” 的格式讲名字作为该类型的同义词的。
2.5.2 auto类型说明符
auto 是一个类型说明符,通过变量的初始值来推断变量的类型。
2.5.3 decltype类型指示符
(1)decltype是一个类型说明符,从变量或表达式推断得到类型。
如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用在内)
— const int ci = 0, &cj = ci;
— decltype(ci) x = 0;
— decltype(cj) y = x; // 引用必须初始化
2.6 自定义数据结构
(1)类成员(class member)类的组成成分。
(2)数据成员(data member)组成对象的数据元素,类的每个对象都有类的数据成员的一份拷贝。数据成员可以在类内部声明的同时初始化。
(3)类内初始值(in-class initializer)在声明类的数据成员时同时提供的初始值,必须置于等号右侧或花括号内。
(4)成员(member)类的组成部分。
(5)struct 是一个关键字,用于定义类。
(6)预处理器(preprocessor)在C++编译过程中执行的一段程序。
(7)预处理变量(preprocessor variable)由处理器管理的变量。在程序编译之前,预处理器负责将程序中的预处理变量替换成它的真实值。
(8)头文件保护符(header guard)使用预处理变量以防止头文件被某个文件重复包含。
(9)
define 是一条预处理指令,用于定义一个预处理变量。
endif是一条预处理指令,用于结束一个#ifdef或#ifndef区域。
ifdef 是一条预处理指令,用于判断给定的变量是否已经定义。
ifndef是一条预处理指令,用于判断给定的变量是否尚未定义。