5-x 第五章总结与测验
章节回顾
常量constant是指在程序执行过程中不可改变的值。C++支持两种常量类型:命名常量和字面量常量。
命名常量named constant是与标识符关联的常量值。字面量常量Literal constant是不与标识符关联的常量值。
值不可变的变量称为常量变量constant variable。可通过const关键字使变量成为常量。常量变量必须初始化。在值传递或值返回时应避免使用const。
类型限定符type qualifier是修饰类型行为的关键词。截至C++23标准,C++仅支持const和volatile作为类型限定符。
常量表达式constant expression是指可在编译时求值的表达式。非常量表达式有时称为运行时表达式runtime expression。
编译时常量compile-time constant是指其值在编译时已知的常量。运行时常量runtime constant是指其初始化值需在运行时才确定的常量。
constexpr 变量必须是编译时常量,并用常量表达式初始化。函数参数不能是 constexpr。
字面量Literals是直接嵌入代码的值。字面量具有类型,可通过字面量后缀改变其默认类型。
魔数magic number是指含义模糊或可能需要后期修改的字面量(通常为数字)。请勿在代码中使用魔数,应改用符号常量。
日常生活中我们使用十进制decimal计数,该系统包含10个数字。计算机采用二进制binary,仅有2个数字。C++还支持八进制octal(基数8)和十六进制hexadecimal(基数16)。这些都属于数制范例numeral systems,即用于表示数字的符号集合(数字)。
字符串string是由连续字符组成的集合,用于表示文本(如名称、单词和句子)。字符串常量始终用双引号括起。C++中的字符串常量采用C风格字符串,其类型特殊且难以操作。
std::string提供了处理文本字符串的便捷安全方式。该类定义在
std::string_view提供对现有字符串(C 风格字符串常量、std::string 或 char 数组)的只读访问,且无需复制。若 std::string_view 指向已被销毁的字符串,则称为悬空dangling视图。当 std::string 被修改时,所有指向该字符串的视图均失效invalidated。使用无效视图(除非重新验证其有效性)将导致未定义行为。
由于C风格字符串常量在整个程序中存在,因此将std::string_view设置为C风格字符串常量是可行的,甚至可将此类视图作为函数返回值。
子字符串substring是指现有字符串中连续的字符序列。
测验时间
问题 #1
为什么命名常量通常比字面常量更优?
显示解答
在程序中使用字面常量(又称魔数)会降低程序的可读性与可维护性。符号常量(命名常量)有助于记录数字的实际含义,且在声明处修改符号常量即可同步更新其所有使用位置的值。
为什么 const/constexpr 变量通常比 #define 符号常量更优?
显示解答
#define 常量不会在调试器中显示,且更容易发生命名冲突。
问题 #2
找出以下代码中的3个问题:
#include <cstdint> // for std::uint8_t
#include <iostream>
int main()
{
std::cout << "How old are you?\n";
std::uint8_t age{};
std::cin >> age;
std::cout << "Allowed to drive a car in Texas: ";
if (age >= 16)
std::cout << "Yes";
else
std::cout << "No";
std::cout << '.\n';
return 0;
}
期望输出示例:
How old are you?
6
Allowed to drive a car in Texas: No
How old are you?
19
Allowed to drive a car in Texas: Yes


显示答案
- 在第8行,年龄被定义为std::uint8_t类型。由于std::uint8_t通常被定义为char类型,在此处使用它会导致程序行为如同输入输出字符值而非数值。例如,若用户输入年龄为“18”,程序仅会提取字符'1'。由于1的ASCII值为49,系统将判定用户年龄为49岁。由于年龄值无需特定最小整数宽度,应使用常规int类型存储。同时可移除#include
。 - 第13行使用了魔数16。尽管上下文能明确其含义,但更推荐使用常量表达式constexpr变量赋值16。
- 第18行中的'.\n'是多字符字面量,会输出错误值。应改为双引号表示(“.\n”)。
问题 #3
std::string 与 std::string_view 的主要区别是什么?
使用 std::string_view 时可能出现什么问题?
显示答案
std::string 提供可修改的字符串。其初始化和复制成本较高。
std::string_view 提供对其他位置字符串的只读视图。其初始化和复制成本较低。当被查看的字符串在 std::string_view 销毁前被销毁时,std::string_view 可能存在危险。
问题 #4
编写一个程序,询问两个人的姓名和年龄,然后输出哪个人的年龄更大。
以下是程序运行的一次示例输出:
Enter the name of person #1: John Bacon
Enter the age of John Bacon: 37
Enter the name of person #2: David Jenkins
Enter the age of David Jenkins: 44
David Jenkins (age 44) is older than John Bacon (age 37).

显示提示
提示:使用 std::getline() 输入该人的姓名。
显示解答
#include <iostream>
#include <string>
#include <string_view>
std::string getName(int num)
{
std::cout << "Enter the name of person #" << num << ": ";
std::string name{};
std::getline(std::cin >> std::ws, name); // read a full line of text into name
return name;
}
int getAge(std::string_view sv)
{
std::cout << "Enter the age of " << sv << ": ";
int age{};
std::cin >> age;
return age;
}
void printOlder(std::string_view name1, int age1, std::string_view name2, int age2)
{
if (age1 > age2)
std::cout << name1 << " (age " << age1 <<") is older than " << name2 << " (age " << age2 <<").\n";
else
std::cout << name2 << " (age " << age2 <<") is older than " << name1 << " (age " << age1 <<").\n";
}
int main()
{
const std::string name1{ getName(1) };
const int age1 { getAge(name1) };
const std::string name2{ getName(2) };
const int age2 { getAge(name2) };
printOlder(name1, age1, name2, age2);
return 0;
}
问题 #5
在上题的解答中,为什么 main 中的变量 age1 不能是 constexpr?
显示解答
constexpr 变量需要常量表达式初始化器,而 getAge() 的调用在常量表达式中是不被允许的。因此,我们只能将该变量声明为 const。

浙公网安备 33010602011771号