快速回顾
复合数据类型(也称为组合数据类型)是可以由基本数据类型(或其他复合数据类型)构造的数据类型。
表达式的值类别表示表达式解析为值、函数还是某种类型的对象。
左值是求值为具有身份的函数或对象的表达式。具有身份的对象或函数具有标识符或可识别的内存地址。左值有两种子类型:可修改左值是可修改的左值,而不可修改左值是其值不可修改的左值(通常因为它们是const或constexpr)。
右值是既非左值也非左值的表达式。这包括字面值(字符串字面值除外)以及函数或运算符的返回值(按值返回时)。
引用是现有对象的别名。一旦引用被定义,对引用的任何操作都会应用于被引用的对象。C++包含两种类型的引用:左值引用和右值引用。左值引用(通常简称为引用)充当现有左值(例如变量)的别名。左值引用变量是充当左值(通常是另一个变量)引用的变量。
当引用用对象(或函数)初始化时,我们说它绑定到该对象(或函数)。被引用的对象(或函数)有时称为被引用者。
左值引用不能绑定到不可修改的左值或右值(否则您将能够通过引用更改这些值,这将违反它们的const属性)。因此,左值引用有时被称为非const左值引用(有时缩写为非const引用)。
一旦初始化,C++中的引用不能被重新绑定,这意味着它不能更改为引用另一个对象。
当被引用的对象在指向它的引用之前被销毁时,引用将指向一个不再存在的对象。这样的引用称为悬空引用。访问悬空引用会导致未定义行为。
通过在声明左值引用时使用const
关键字,我们告诉左值引用将其引用的对象视为const。这样的引用称为指向const值的左值引用(有时称为const引用或const引用)。const引用可以绑定到可修改的左值、不可修改的左值和右值。
临时对象(有时也称为无名对象或匿名对象)是在单个表达式中为临时使用而创建(然后销毁)的对象。
使用按引用传递时,我们将函数参数声明为引用(或const引用),而不是普通变量。当函数被调用时,每个引用参数都绑定到相应的实参。因为引用充当实参的别名,所以不会创建实参的副本。
取地址运算符 (&) 返回其操作数的内存地址。解引用运算符 (*) 返回给定内存地址处的值作为左值。
指针是持有内存地址(通常是另一个变量的内存地址)作为其值的对象。这允许我们存储其他一些对象的地址以供以后使用。像普通变量一样,指针默认不初始化。未初始化的指针有时称为野指针。悬空指针是持有不再有效(例如,因为它已被销毁)的对象的地址的指针。
除了内存地址,指针还可以包含一个额外的值:空值。空值(通常缩写为空)是一个特殊值,表示某物没有值。当指针包含空值时,表示该指针未指向任何内容。这样的指针称为空指针。nullptr关键字表示空指针字面值。我们可以使用nullptr
来显式初始化或将空值分配给指针。
指针应持有有效对象的地址,或设置为nullptr
。这样我们只需要测试指针是否为空,并可以假定任何非空指针都是有效的。
指向const值的指针(有时简称为指向const的指针)是(非const)指向常量值的指针。
const指针是初始化后其地址不能更改的指针。
指向const值的const指针既不能更改其地址,也不能通过该指针更改其指向的值。
使用按地址传递,调用者不提供对象作为参数,而是提供对象的地址(通过指针)。该指针(持有对象的地址)被复制到被调用函数的指针参数中(该参数现在也持有对象的地址)。然后函数可以解引用该指针以访问传递了地址的对象。
按引用返回返回绑定到被返回对象的引用,这避免了创建返回值的副本。使用按引用返回有一个主要注意事项:程序员必须确保被引用的对象在返回引用的函数之外仍然存在。否则,返回的引用将悬空(引用已被销毁的对象),并且使用该引用将导致未定义行为。如果参数通过引用传递到函数中,则可以安全地通过引用返回该参数。
如果函数返回一个引用,并且该引用用于初始化或赋值给非引用变量,则返回值将被复制(就像按值返回一样)。
变量的类型推导(通过auto
关键字)将从推导出的类型中删除任何引用或顶层const限定符。如果需要,可以在变量声明中重新应用它们。
按地址返回的工作方式几乎与按引用返回相同,只是返回的是指向对象的指针而不是对对象的引用。
小测验时间
问题 #1
对于运算符 << 右侧的以下每个表达式,指示表达式是左值还是右值
a)
std::cout << 5;
b)
int x { 5 };
std::cout << x;
c)
int x { 5 };
std::cout << x + 1;
d)
int foo() { return 5; }
std::cout << foo();
e)
int& max(int &x, int &y) { return x > y ? x : y; }
int x { 5 };
int y { 6 };
std::cout << max(x, y);
问题 #2
此程序的输出是什么?
#include <iostream>
int main()
{
int x{ 4 };
int y{ 6 };
int& ref{ x };
++ref;
ref = y;
++ref;
std::cout << x << ' ' << y;
return 0;
}
问题 #3
说明我们尽可能地偏好通过 const 引用而不是非 const 引用传递参数的两个原因。
问题 #4
const指针和指向const的指针有什么区别?
问题 #5
编写一个名为 sort2
的函数,允许调用者传递 2 个 int 变量作为参数。函数返回时,第一个参数应保存两个值中较小的一个,第二个参数应保存两个值中较大的一个。
提示:std::swap()
函数(位于 <algorithm> 头文件中)可用于交换两个变量的值。例如:std::swap(x, y)
交换变量 x
和 y
的值。
以下代码应运行并打印注释中指出的值
#include <iostream>
int main()
{
int x { 7 };
int y { 5 };
std::cout << x << ' ' << y << '\n'; // should print 7 5
sort2(x, y); // make sure sort works when values need to be swapped
std::cout << x << ' ' << y << '\n'; // should print 5 7
sort2(x, y); // make sure sort works when values don't need to be swapped
std::cout << x << ' ' << y << '\n'; // should print 5 7
return 0;
}