Skip to content

回顾

将复习 C++ Primer 从开始到 2.4节(不包括)前的内容。

注意,该文档仅仅简单提及要点,真正的复习需要仔细看书,并将遗漏的知识点不足。

弱类型

C++ 是弱类型的语言,弱类型即允许隐式类型转换

在 C++ 中,我们允许这样的代码出现

double d = 0.0;
int i = d; // 发生隐式类型转换

该代码将一个 double 赋给 int,此处发生 doubleint 的自动转换。

与弱类型相反,在强类型的编程语言中,任何隐式转换都不允许发生,因此不允许两个不同类型的变量相互赋值。

请仔细阅读 C++ Primer 第 33 页开头部分(不同类型隐式转换的过程)。

有无符号类型的隐式转换

有符号类型与无符号类型同时出现的表达式中,有符号类型隐式转换为无符号类型。

auto i = -1 + unsigned(0); 
// 结果是 unsigned int 这个类型能表示的最大值

初始化

拷贝初始化

  • 拷贝初始化:double i = 0.0

  • 直接初始化:double i(0.0)

  • 列表初始化:double i{0.0}

当存在信息丢失风险的时候,列表初始化会报错!

int i = 0.0; // ok
int i1(0.0); // ok
int i2{0.0}; // wrong

若一个变量没有初始化,例如 int i;,那它的值是未定义的,这种情况比较危险,因此推荐每次定义变量都初始化。

Scope Rules(作用域规则)

两类作用域

  • 全局作用域

  • 块作用域

全局变量都是处在全局作用域中。

作用域嵌套规则

  • 内层作用域有权利访问外层作用域中的变量
  • 内存作用域中的同名变量,在名称查找中覆盖外层中的变量

使用作用域运算符 :: 可以强制指定其它作用域中的名字。

#include <iostream>

int a = 0;
int main() {
  int a = 1;
  std::cout << a << ' ' << ::a << std::endl;
  // Prints "1 0"
}

复合类型

引用

引用定义了一个别名

int a = 0;
int &ra = a;

这里的 ra 仅仅是 a 的别名。可以理解为,任何使用 ra 的情况都等价于使用 a 的情况(不完全准确)。因此,引用本身是不占用内存空间的,即引用类型不是对象

只能绑定对象

一个引用只能绑定对象,即只能绑定到占用内存空间的变量、值。

int a = 0;
int &ra = a; // ok
int &rx = 0; // wrong, 0 不是对象
int &ra1 = ra; // ok, 这里的 ra 只是 a 的别名 

引用不是对象

再次强调,引用不是对象。一个指针需要指向一个对象,即一个内存空间。而由于引用不是对象,所以指针不能指向一个引用。

int &*p = ra; // wrong

ra 沿用之前定义。这段代码中,p 是个指向引用的指针,这是不允许的。

指针

本身是对象,指向一个对象。

指针的概念无需赘述,在此介绍一些特殊指针。

空指针

nullptr 是空指针字面值常量,任何类型的指针都可用它赋值来置空。

0NULL 也可以当作空指针使用,但不推荐

int *p = nullptr; // recommended
p = 0; // not recommended
// 需要先 #include <cstdlib>
p = NULL; // not recommended

注意

int 赋值给指针是错误的,即使它恰好为 0。

int zero = 0;
int *p = zero; // wrong

初始化

指针不初始化会指向某个随机的地址,那个地址上的数据或许根本不存在,因此建议指针必须初始化

Void * 指针

该指针可以赋为任意类型的指针。

int *p = nullptr;
double *p1 = nullptr;

void *any = p;
any = p1;

这样做可以擦除类型,但不建议这样使用。

理解复合类型的声明

int* p, i; // p 是指针,i 是 int。
int *p1, **pp; // p1 是指针,pp 是指向指针的指针

从内向外理解类型

int *&r = ...; // “指针”的引用
int **&r1 = ...; // “指向指针的指针”的引用