章节回顾
固定长度数组(fixed-size arrays)要求在实例化时即已知长度,且之后不可更改。C 风格数组与 std::array 均属此类;动态数组可在运行时调整大小,std::vector 即为动态数组。
std::array 的长度必须是一个常量表达式;通常以整数字面量、constexpr 变量或无作用域枚举值给出。
std::array 是一个聚合体(aggregate),因此没有构造函数,只能使用聚合初始化。
只要可行,请将 std::array 声明为 constexpr;若非如此,请优先考虑 std::vector。
使用类模板实参推导(CTAD)让编译器根据初始值推断 std::array 的元素类型与长度。
std::array 的实现形如:
template<typename T, std::size_t N> // N 是非类型模板形参
struct array;获取 std::array 长度:
• 成员函数 size()(返回无符号 size_type)。
• C++17 非成员函数 std::size()(同上)。
• C++20 非成员函数 std::ssize()(返回带符号整数,通常为 std::ptrdiff_t)。
三者均返回 constexpr 值;但若形参为引用,则返回非 constexpr(C++23 P2280 已修正)。索引 std::array:
• operator[] 无边界检查,越界产生未定义行为。
• at() 运行时检查边界;建议避免,除非预先检查或在编译期检查。
• std::get(arr) 以非类型模板实参 I 在编译期检查边界。传递不同元素类型或长度的 std::array 给函数时,可使用
template <typename T, std::size_t N> 或 C++20 的 template <typename T, auto N>。按值返回 std::array 会复制整个数组;若数组较小且元素拷贝开销低,可接受;否则考虑使用输出参数。
用聚合初始化 std::array 且元素为结构体、类或数组时,若未显式写出元素类型,需额外一对花括号;这是聚合初始化的特性,标准库其它容器(采用列表构造函数)则无需双层花括号。
C++ 聚合支持花括号省略(brace elision):以标量值初始化,或每个元素显式写出类型时可省略多余花括号。
不可拥有“引用数组”,但可使用 std::reference_wrapper 数组,其行为类似可修改的左值引用:
operator=
可重定向引用目标。
std::reference_wrapper<T>
可隐式转换为T&
。get()
可显式取得T&
。- 辅助函数
std::ref()
、std::cref()
用于快速创建包装对象。
用 static_assert 确保经 CTAD 得到的 constexpr std::array 拥有正确数量的初始值。
C 风格数组继承自 C,属于核心语言特性,具有专用声明语法:
int a[5]; // 方括号内为长度,类型为 std::size_t 的常量表达式。
16. C 风格数组亦为聚合,可用聚合初始化;若用初始化列表为所有元素赋值,建议省略长度,由编译器推导。
17. C 风格数组可用 operator[] 索引;下标可为带符号 / 无符号整数或无作用域枚举,因此不存在符号转换带来的索引问题。
18. C 风格数组可以是 const 或 constexpr。
19. 获取 C 风格数组长度:
• C++17 非成员函数 std::size()(返回 std::size_t)。
• C++20 非成员函数 std::ssize()(返回带符号整数)。
20. 在表达式中,C 风格数组通常隐式转换为指向首元素的指针(数组退化)。
21. 指针算术允许对指针进行加减、自增、自减,以产生新的地址;ptr + 1 指向内存中下一对象。
22. 从数组起始索引请使用下标;相对当前元素定位请使用指针算术。
23. C 风格字符串即元素类型为 char 或 const char 的 C 风格数组,同样会发生退化。
24. 数组维度指选取元素所需下标数量。
• 单维度称一维数组;
• 数组的数组称二维数组;
• 维度多于一个统称多维数组;
• 扁平化(flattening)指将多维数组降维至一维。
25. C++23 的 std::mdspan 为连续元素序列提供多维数组视图。
## 测验
问题 1
指出以下代码片段中的错误并给出修正方法。
```cpp
a)
#include <array>
#include <iostream>
int main()
{
std::array arr{ 0, 1, 2, 3 };
for (std::size_t count{ 0 }; count <= std::size(arr); ++count)
{
std::cout << arr[count] << ' ';
}
std::cout << '\n';
return 0;
}
(答案略)
b)
#include <iostream>
void printArray(int array[])
{
for (int element : array) { std::cout << element << ' '; }
}
int main()
{
int array[]{ 9, 7, 5, 3, 1 };
printArray(array);
std::cout << '\n';
}
(答案略)
c)
#include <array>
#include <iostream>
int main()
{
std::cout << "Enter the number of test scores: ";
std::size_t length{};
std::cin >> length;
std::array<int, length> scores;
…
}
(答案略)
问题 2
实现“Roscoe 药剂商店”。要求详见原题,分三步完成。
(步骤 1、2、3 的提示与解答略)
问题 3
为纸牌游戏构建卡牌与牌组功能。
(步骤 1–4 的提示与解答略)
问题 4
基于上题代码实现简化版二十一点(Blackjack)。
(步骤 1–3 的提示与解答略)
问题 5
a) 描述如何修改程序以支持 A 既可以当 1 又可当 11。
b) 描述如何修改程序以支持平局(分数相同且未爆牌)。
c) 加分项:将以上两点实现,并展示示例输出。
(答案略)