基本内置类型

选择类型

  • 当明确知晓数值不可能为负时,选用无符号类型
  • 用 int 执行整数运算,若 int 不够大则选择 long long
  • 算术表达式中不要使用 char / bool
  • 浮点数运算选用 double

当赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。例如,8 比特大小的 unsigned char 可以表示 0 至 255 区间内的值,如果我们赋给了一个区间以外的值,则实际的结果是该值对 256 取模后的余数。因此,把 -1 赋给 8 比特大小的 unsigned char 所得的结果是 255

变量

  1. int units_sold = 0;
  2. int units_sold = {0}; // 列表初始化
  3. int units_sold{0} // 列表初始化
  4. int units_sold(0);

如果我们使用列表初始化且初始值存在丢失信息的风险,则编译器将报错。

  1. long double ld = 3;
  2. int a{ld}, b = {ld}; // 错误
  3. int c(ld), e = ld; // 正确

为了支持分离式编译,C++语言将声明和定义区分开来。一个文件如果想使用别处定义的名字则必须包含对那个名字的声明。如果想声明一个变量而非定义它,就在变量名前添加关键字 extern 。

extern int i;  // 声明 i 而非定义 i
int j;  // 声明并定义 j
extern double pi = 3.1415926;  // 赋值将抵消 extern 的作用,这行代码定义了 pi

若局部变量和全局变量 reused 重名,则可在局部变量作用域内使用 : : reused 来显式访问全局变量。因为全局作用域本身没有名字,所以作用域操作符 : : 的左侧为空。

复合类型

引用并非对象,它只是为一个已经存在的对象所起的另外一个名字。因为引用本身并不是一个对象,所以不能定义引用的引用。引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起。
指针本身是一个对象,所以有指向指针的指针,但不能定义指向引用的指针。

生成空指针的方法

int *p1 = nullptr;  // 推荐
int *p2 = 0;
int *p3 = NULL;

存在指向指针的引用

int *&r = p;  // r 是对指针 p 的引用

要理解 r 的类型是什么,最简单的办法是从右向左阅读 r 的定义。离变量名最近的符号对变量的类型有最直接的影响

const限定符

const 对象一旦创建后不能修改,故 const 对象必须初始化。

如果想在多个文件间共享 const 对象,必须在变量定义前添加 extern 关键字

extern const int bufSize = fcn();

引用与指针的类型必须与所指对象的类型完全一致,const 对象也不例外。(有例外)

const int ci = 1024;
int &ri = ci;  // 错误,引用与对象的类型必须完全一致

例外1:初始化常量引用时允许用任意表达式作为初始值,只要表达式的结果可以转换成引用的类型。

int i = 42;
const int &r1 = i;  // int 
const int &r2 = 42;  // 字面值
const int &r3 = r1 * 2;  // 表达式
int &r4 = r1 * 2;  // 错误,r4 不是常量引用

// 若常量引用被绑定到了另一种类型,则它绑定了一个临时量
// 源代码
double d = 3.14;
const int &r = d;
// 编译器将上述代码变成了如下形式
const int temp = d;
const int &r = temp;
// 故修改 d 不能改变 r 的值

例外2:指向常量的指针(底层 const)可以指向一个非常量对象,但它们的类型必须一致。

  • 顶层 const :指针本身是个常量
  • 底层 const :指针所指的对象是个常量,这是指针或引用一厢情愿的

因为非底层 const 会赋予指针或引用改变变量值的权限,所以只有底层 const 才可以绑定常量对象

常量表达式:值不会改变并且在编译过程就能得到结果的表达式。

constexpr int mf = 20;  // 20 是常量表达式
constexpr int limit = mf + 1;  // mf + 1 是常量表达式

处理类型

类型别名

typedef double wages, *p;  // wages 是 double 的同义词,p 是 double * 的同义词
using d = double;  // d 是 double 的同义词

auto 类型说明符可以让编译器替我们分析表达式所属的类型,显然,auto 定义的变量必须有初始值。

auto i = 0, pi = 3.14;  // 错误,一条声明语句只能有一个基本数据类型
int i = 0, &r = i;
auto a = r;  // auto 会忽略引用,a 为 int 类型
const int ci = i;
auto b = ci;  // auto 会忽略顶层 const,b 为 int 类型
const auto &j = 42;  // 如需保留 [顶层const|引用] 属性,则要明确指出

decltype 可以获取表达式的数据类型

int i = 42, *p = &i, &r = i;
decltype(r + 0) b;  // int
decltype(*p) c;  // 错误,如果表达式的内容为解引用操作,则 decltype 将得到引用类型,而引用类型必须初始化
decltype((i)) d;  // 错误,表达式的内容为双括号,将得到引用类型

decltype 并不计算表达式的值(不执行表达式)。