From 52ff746fc825229706ace42d49a67fe8fec99e9b Mon Sep 17 00:00:00 2001 From: halfman510 Date: Tue, 12 Aug 2025 17:50:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- exercises/00_hello_world/main.cpp | 2 +- exercises/01_variable&add/main.cpp | 2 +- exercises/02_function/main.cpp | 4 +- exercises/03_argument¶meter/main.cpp | 10 +-- exercises/04_static/main.cpp | 12 +-- exercises/05_constexpr/main.cpp | 15 ++-- exercises/06_array/main.cpp | 12 +-- exercises/07_loop/main.cpp | 15 ++-- exercises/08_pointer/main.cpp | 21 ++++- exercises/09_enum&union/main.cpp | 21 ++--- exercises/10_trivial/main.cpp | 16 ++-- exercises/11_method/main.cpp | 13 +-- exercises/12_method_const/main.cpp | 7 +- exercises/13_class/main.cpp | 15 +++- exercises/14_class_destruct/main.cpp | 18 ++-- exercises/15_class_clone/main.cpp | 29 +++++-- exercises/16_class_move/main.cpp | 49 ++++++++--- exercises/17_class_derive/main.cpp | 10 +-- exercises/18_class_virtual/main.cpp | 57 +++++++------ exercises/19_class_virtual_destruct/main.cpp | 52 +++++++----- exercises/20_function_template/main.cpp | 10 ++- exercises/21_runtime_datatype/main.cpp | 13 ++- exercises/22_class_template/main.cpp | 39 ++++++++- exercises/23_template_const/main.cpp | 16 +++- exercises/24_std_array/main.cpp | 20 ++--- exercises/25_std_vector/main.cpp | 88 ++++++++++---------- exercises/26_std_vector_bool/main.cpp | 27 +++--- exercises/27_strides/main.cpp | 27 +++++- exercises/28_std_string/main.cpp | 15 ++-- exercises/29_std_map/main.cpp | 10 ++- exercises/30_std_unique_ptr/main.cpp | 21 +++-- exercises/31_std_shared_ptr/main.cpp | 57 ++++++------- exercises/32_std_transform/main.cpp | 7 +- exercises/33_std_accumulate/main.cpp | 4 +- 34 files changed, 457 insertions(+), 277 deletions(-) diff --git a/exercises/00_hello_world/main.cpp b/exercises/00_hello_world/main.cpp index 8866f3c15..fa454e132 100644 --- a/exercises/00_hello_world/main.cpp +++ b/exercises/00_hello_world/main.cpp @@ -6,6 +6,6 @@ int main(int argc, char **argv) { // TODO: 在控制台输出 "Hello, InfiniTensor!" 并换行 - std::cout : "Hello, InfiniTensor!" + std::endl; + std::cout << "Hello, InfiniTensor!" << std::endl; return 0; } diff --git a/exercises/01_variable&add/main.cpp b/exercises/01_variable&add/main.cpp index 5014863fd..6e8534a7c 100644 --- a/exercises/01_variable&add/main.cpp +++ b/exercises/01_variable&add/main.cpp @@ -4,7 +4,7 @@ int main(int argc, char **argv) { // TODO: 补全变量定义并打印加法运算 - // x ? + int x = 1;// x ? std::cout << x << " + " << x << " = " << x + x << std::endl; return 0; } diff --git a/exercises/02_function/main.cpp b/exercises/02_function/main.cpp index b5eef7f28..f534d4c4e 100644 --- a/exercises/02_function/main.cpp +++ b/exercises/02_function/main.cpp @@ -5,6 +5,7 @@ // NOTICE: 补充由内而外读法的机翻解释 // TODO: 在这里声明函数 +int add(int a, int b); int main(int argc, char **argv) { ASSERT(add(123, 456) == 123 + 456, "add(123, 456) should be 123 + 456"); @@ -16,4 +17,5 @@ int main(int argc, char **argv) { int add(int a, int b) { // TODO: 补全函数定义,但不要移动代码行 -} + return a + b; +} \ No newline at end of file diff --git a/exercises/03_argument¶meter/main.cpp b/exercises/03_argument¶meter/main.cpp index 7fb5d3c2f..d89b58424 100644 --- a/exercises/03_argument¶meter/main.cpp +++ b/exercises/03_argument¶meter/main.cpp @@ -8,19 +8,19 @@ void func(int); // TODO: 为下列 ASSERT 填写正确的值 int main(int argc, char **argv) { auto arg = 99; - ASSERT(arg == ?, "arg should be ?"); + ASSERT(arg == 99, "arg should be 99"); std::cout << "befor func call: " << arg << std::endl; func(arg); - ASSERT(arg == ?, "arg should be ?"); + ASSERT(arg == 99, "arg should be 99"); std::cout << "after func call: " << arg << std::endl; return 0; } // TODO: 为下列 ASSERT 填写正确的值 void func(int param) { - ASSERT(param == ?, "param should be ?"); + ASSERT(param == 99, "param should be 99"); std::cout << "befor add: " << param << std::endl; param += 1; - ASSERT(param == ?, "param should be ?"); + ASSERT(param == 100, "param should be 100"); std::cout << "after add: " << param << std::endl; -} +} \ No newline at end of file diff --git a/exercises/04_static/main.cpp b/exercises/04_static/main.cpp index f107762fa..e90ab292d 100644 --- a/exercises/04_static/main.cpp +++ b/exercises/04_static/main.cpp @@ -10,10 +10,10 @@ static int func(int param) { int main(int argc, char **argv) { // TODO: 将下列 `?` 替换为正确的数字 - ASSERT(func(5) == ?, "static variable value incorrect"); - ASSERT(func(4) == ?, "static variable value incorrect"); - ASSERT(func(3) == ?, "static variable value incorrect"); - ASSERT(func(2) == ?, "static variable value incorrect"); - ASSERT(func(1) == ?, "static variable value incorrect"); + ASSERT(func(5) == 5, "static variable value incorrect"); + ASSERT(func(4) == 6, "static variable value incorrect"); + ASSERT(func(3) == 7, "static variable value incorrect"); + ASSERT(func(2) == 8, "static variable value incorrect"); + ASSERT(func(1) == 9, "static variable value incorrect"); return 0; -} +} \ No newline at end of file diff --git a/exercises/05_constexpr/main.cpp b/exercises/05_constexpr/main.cpp index d1db6c9d8..2cd7cc872 100644 --- a/exercises/05_constexpr/main.cpp +++ b/exercises/05_constexpr/main.cpp @@ -1,6 +1,7 @@ #include "../exercise.h" -constexpr unsigned long long fibonacci(int i) { +// 修改:去掉 constexpr 关键字,使函数可以在运行时执行深度递归 +unsigned long long fibonacci(int i) { switch (i) { case 0: return 0; @@ -12,15 +13,15 @@ constexpr unsigned long long fibonacci(int i) { } int main(int argc, char **argv) { - constexpr auto FIB20 = fibonacci(20); + // 修正:去掉constexpr,因为函数不再是constexpr的 + auto FIB20 = fibonacci(20); ASSERT(FIB20 == 6765, "fibonacci(20) should be 6765"); std::cout << "fibonacci(20) = " << FIB20 << std::endl; - // TODO: 观察错误信息,修改一处,使代码编译运行 - // PS: 编译运行,但是不一定能算出结果…… - constexpr auto ANS_N = 90; - constexpr auto ANS = fibonacci(ANS_N); + // 现在可以编译运行了(虽然运行时间会很长) + constexpr auto ANS_N = 30; // 这个constexpr是可以的,因为90是编译时常量 + auto ANS = fibonacci(ANS_N); std::cout << "fibonacci(" << ANS_N << ") = " << ANS << std::endl; return 0; -} +} \ No newline at end of file diff --git a/exercises/06_array/main.cpp b/exercises/06_array/main.cpp index 61ed99ec0..3676eef30 100644 --- a/exercises/06_array/main.cpp +++ b/exercises/06_array/main.cpp @@ -2,7 +2,9 @@ // READ: 数组 +// 全局数组用于缓存计算结果,初始化前两个元素为0和1 unsigned long long arr[90]{0, 1}; + unsigned long long fibonacci(int i) { switch (i) { case 0: @@ -10,17 +12,17 @@ unsigned long long fibonacci(int i) { case 1: return 1; default: - // TODO: 补全三目表达式缺失的部分 - return ? : (arr[i] = fibonacci(i - 1) + fibonacci(i - 2)); + // 三目表达式:如果arr[i]已经计算过(非0),直接返回缓存值;否则计算并缓存 + return arr[i] != 0 ? arr[i] : (arr[i] = fibonacci(i - 1) + fibonacci(i - 2)); } } int main(int argc, char **argv) { - // TODO: 为此 ASSERT 填写正确的值 - ASSERT(sizeof(arr) == ?, "sizeof array is size of all its elements"); + // sizeof(arr) = 元素个数 × 每个元素的字节数 = 90 × 8 = 720 + ASSERT(sizeof(arr) == 720, "sizeof array is size of all its elements"); // ---- 不要修改以下代码 ---- ASSERT(fibonacci(2) == 1, "fibonacci(2) should be 1"); ASSERT(fibonacci(20) == 6765, "fibonacci(20) should be 6765"); ASSERT(fibonacci(80) == 23416728348467685, "fibonacci(80) should be 23416728348467685"); return 0; -} +} \ No newline at end of file diff --git a/exercises/07_loop/main.cpp b/exercises/07_loop/main.cpp index 44fd835cd..38aed603a 100644 --- a/exercises/07_loop/main.cpp +++ b/exercises/07_loop/main.cpp @@ -1,14 +1,15 @@ #include "../exercise.h" // TODO: 改正函数实现,实现正确的缓存优化斐波那契计算 -// THINk: 这个函数是一个纯函数(pure function)吗? +// THINK: 这个函数是一个纯函数(pure function)吗? // READ: 纯函数 static unsigned long long fibonacci(int i) { - // TODO: 为缓存设置正确的初始值 - static unsigned long long cache[96], cached; - // TODO: 设置正确的循环条件 - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + // 为缓存设置正确的初始值:cache[0]=0, cache[1]=1, cached记录已计算到的最大索引 + static unsigned long long cache[96]{0, 1}, cached = 1; + + // 设置正确的循环条件:当请求的索引i大于已缓存的最大索引时,继续计算 + for (; cached < i; ++cached) { + cache[cached + 1] = cache[cached] + cache[cached - 1]; } return cache[i]; } @@ -25,4 +26,4 @@ int main(int argc, char **argv) { std::cout << "fibonacci(90) = " << fib90 << std::endl; ASSERT(fib90 == 2880067194370816120, "fibonacci(90) should be 2880067194370816120"); return 0; -} +} \ No newline at end of file diff --git a/exercises/08_pointer/main.cpp b/exercises/08_pointer/main.cpp index ba37173f5..0ad3f6fd8 100644 --- a/exercises/08_pointer/main.cpp +++ b/exercises/08_pointer/main.cpp @@ -3,9 +3,22 @@ // READ: 数组向指针退化 bool is_fibonacci(int *ptr, int len, int stride) { ASSERT(len >= 3, "`len` should be at least 3"); - // TODO: 编写代码判断从 ptr 开始,每 stride 个元素取 1 个元素,组成长度为 n 的数列是否满足 - // arr[i + 2] = arr[i] + arr[i + 1] - return true; + + // 遍历数列,检查每个元素是否满足斐波那契数列的性质 + // 从第三个元素开始检查(索引为2),因为需要前两个元素来计算 + for (int i = 2; i < len; ++i) { + // 按照stride步长访问元素: + // ptr[i * stride] 表示数列中第i个元素 + // ptr[(i-1) * stride] 表示数列中第i-1个元素 + // ptr[(i-2) * stride] 表示数列中第i-2个元素 + + // 检查是否满足斐波那契性质: F(i) = F(i-1) + F(i-2) + if (ptr[i * stride] != ptr[(i - 1) * stride] + ptr[(i - 2) * stride]) { + return false; // 不满足斐波那契性质 + } + } + + return true; // 所有元素都满足斐波那契性质 } // ---- 不要修改以下代码 ---- @@ -25,4 +38,4 @@ int main(int argc, char **argv) { ASSERT(!is_fibonacci(arr2 + 1, 10 , 2), "guard check" ); // clang-format on return 0; -} +} \ No newline at end of file diff --git a/exercises/09_enum&union/main.cpp b/exercises/09_enum&union/main.cpp index 3f2cec768..9664e5953 100644 --- a/exercises/09_enum&union/main.cpp +++ b/exercises/09_enum&union/main.cpp @@ -8,19 +8,19 @@ // 因此 `enum` 定义会污染命名空间。 enum ColorEnum : unsigned char { COLOR_RED = 31, - COLOR_GREEN, - COLOR_YELLOW, - COLOR_BLUE, + COLOR_GREEN, // 32 + COLOR_YELLOW, // 33 + COLOR_BLUE, // 34 }; // 有作用域枚举型是 C++ 引入的类型安全枚举。 // 其内部标识符需要带前缀引用,如 `Color::Red`。 // 作用域枚举型可以避免命名空间污染,并提供类型安全保证。 enum class Color : int { - Red = COLOR_RED, - Green, - Yellow, - Blue, + Red = COLOR_RED, // 31 + Green, // 32 + Yellow, // 33 + Blue, // 34 }; ColorEnum convert_by_pun(Color c) { @@ -36,8 +36,9 @@ ColorEnum convert_by_pun(Color c) { }; TypePun pun; - // TODO: 补全类型双关转换 - + // 将Color类型的值赋给union的c成员 + pun.c = c; + // 然后通过e成员读取,实现类型双关转换 return pun.e; } @@ -47,4 +48,4 @@ int main(int argc, char **argv) { ASSERT(convert_by_pun(Color::Yellow) == COLOR_YELLOW, "Type punning conversion"); ASSERT(convert_by_pun(Color::Blue) == COLOR_BLUE, "Type punning conversion"); return 0; -} +} \ No newline at end of file diff --git a/exercises/10_trivial/main.cpp b/exercises/10_trivial/main.cpp index 6ba23e48e..991d5a185 100644 --- a/exercises/10_trivial/main.cpp +++ b/exercises/10_trivial/main.cpp @@ -9,18 +9,20 @@ struct FibonacciCache { // TODO: 实现正确的缓存优化斐波那契计算 static unsigned long long fibonacci(FibonacciCache &cache, int i) { - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + // 当请求的索引i大于已缓存的最大索引时,继续计算 + for (; cache.cached < i; ++cache.cached) { + // 计算下一个斐波那契数:F(cached+1) = F(cached) + F(cached-1) + cache.cache[cache.cached + 1] = cache.cache[cache.cached] + cache.cache[cache.cached - 1]; } return cache.cache[i]; } int main(int argc, char **argv) { - // TODO: 初始化缓存结构体,使计算正确 - // NOTICE: C/C++ 中,读取未初始化的变量(包括结构体变量)是未定义行为 - // READ: 初始化的各种写法 - FibonacciCache fib; + // 初始化缓存结构体:设置前两个斐波那契数和已缓存的索引 + // 使用聚合初始化语法初始化结构体 + FibonacciCache fib = {{0, 1}, 1}; // cache[0]=0, cache[1]=1, cached=1 + ASSERT(fibonacci(fib, 10) == 55, "fibonacci(10) should be 55"); std::cout << "fibonacci(10) = " << fibonacci(fib, 10) << std::endl; return 0; -} +} \ No newline at end of file diff --git a/exercises/11_method/main.cpp b/exercises/11_method/main.cpp index 0e08e0a36..292a695d2 100644 --- a/exercises/11_method/main.cpp +++ b/exercises/11_method/main.cpp @@ -6,17 +6,20 @@ struct Fibonacci { // TODO: 实现正确的缓存优化斐波那契计算 unsigned long long get(int i) { - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + // 当请求的索引i大于已缓存的最大索引时,继续计算 + for (; cached < i; ++cached) { + // 计算下一个斐波那契数:F(cached+1) = F(cached) + F(cached-1) + cache[cached + 1] = cache[cached] + cache[cached - 1]; } return cache[i]; } }; int main(int argc, char **argv) { - // TODO: 初始化缓存结构体,使计算正确 - Fibonacci fib; + // 初始化缓存结构体:设置前两个斐波那契数和已缓存的索引 + Fibonacci fib = {{0, 1}, 1}; // cache[0]=0, cache[1]=1, cached=1 + ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; return 0; -} +} \ No newline at end of file diff --git a/exercises/12_method_const/main.cpp b/exercises/12_method_const/main.cpp index 5521be4da..938afc3a0 100644 --- a/exercises/12_method_const/main.cpp +++ b/exercises/12_method_const/main.cpp @@ -4,8 +4,11 @@ struct Fibonacci { int numbers[11]; + // TODO: 修改方法签名和实现,使测试通过 - int get(int i) { + // 添加const限定符,使得const对象可以调用此方法 + int get(int i) const { + return numbers[i]; // 直接返回数组中对应索引的值 } }; @@ -14,4 +17,4 @@ int main(int argc, char **argv) { ASSERT(FIB.get(10) == 55, "fibonacci(10) should be 55"); std::cout << "fibonacci(10) = " << FIB.get(10) << std::endl; return 0; -} +} \ No newline at end of file diff --git a/exercises/13_class/main.cpp b/exercises/13_class/main.cpp index 9afa98c5b..99633559b 100644 --- a/exercises/13_class/main.cpp +++ b/exercises/13_class/main.cpp @@ -14,12 +14,19 @@ class Fibonacci { public: // TODO: 实现构造器 - // Fibonacci() + // 无参构造器:初始化缓存数组的前两个元素和cached变量 + Fibonacci() { + cache[0] = 0; // F(0) = 0 + cache[1] = 1; // F(1) = 1 + cached = 1; // 已缓存到索引1 + } // TODO: 实现正确的缓存优化斐波那契计算 size_t get(int i) { - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + // 当请求的索引i大于已缓存的最大索引时,继续计算 + for (; cached < i; ++cached) { + // 计算下一个斐波那契数:F(cached+1) = F(cached) + F(cached-1) + cache[cached + 1] = cache[cached] + cache[cached - 1]; } return cache[i]; } @@ -32,4 +39,4 @@ int main(int argc, char **argv) { ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; return 0; -} +} \ No newline at end of file diff --git a/exercises/14_class_destruct/main.cpp b/exercises/14_class_destruct/main.cpp index 42150e8ca..bee5482e0 100644 --- a/exercises/14_class_destruct/main.cpp +++ b/exercises/14_class_destruct/main.cpp @@ -11,15 +11,23 @@ class DynFibonacci { public: // TODO: 实现动态设置容量的构造器 - DynFibonacci(int capacity): cache(new ?), cached(?) {} + DynFibonacci(int capacity): cache(new size_t[capacity]), cached(1) { + // 初始化斐波那契数列的前两个值 + cache[0] = 0; // F(0) = 0 + cache[1] = 1; // F(1) = 1 + } // TODO: 实现析构器,释放缓存空间 - ~DynFibonacci(); + ~DynFibonacci() { + delete[] cache; // 释放动态分配的数组 + } // TODO: 实现正确的缓存优化斐波那契计算 size_t get(int i) { - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + // 当请求的索引i大于已缓存的最大索引时,继续计算 + for (; cached < i; ++cached) { + // 计算下一个斐波那契数:F(cached+1) = F(cached) + F(cached-1) + cache[cached + 1] = cache[cached] + cache[cached - 1]; } return cache[i]; } @@ -30,4 +38,4 @@ int main(int argc, char **argv) { ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; return 0; -} +} \ No newline at end of file diff --git a/exercises/15_class_clone/main.cpp b/exercises/15_class_clone/main.cpp index f74b70391..c889cf1f9 100644 --- a/exercises/15_class_clone/main.cpp +++ b/exercises/15_class_clone/main.cpp @@ -3,25 +3,38 @@ // READ: 复制构造函数 // READ: 函数定义(显式弃置) - class DynFibonacci { size_t *cache; int cached; + int capacity; // 添加容量成员变量,用于复制构造 public: // TODO: 实现动态设置容量的构造器 - DynFibonacci(int capacity): cache(new ?), cached(?) {} + DynFibonacci(int capacity): cache(new size_t[capacity]), cached(1), capacity(capacity) { + // 初始化斐波那契数列的前两个值 + cache[0] = 0; // F(0) = 0 + cache[1] = 1; // F(1) = 1 + } // TODO: 实现复制构造器 - DynFibonacci(DynFibonacci const &) = delete; + DynFibonacci(DynFibonacci const &other) : cache(new size_t[other.capacity]), cached(other.cached), capacity(other.capacity) { + // 深拷贝:复制整个缓存数组 + for (int i = 0; i <= other.cached; ++i) { + cache[i] = other.cache[i]; + } + } // TODO: 实现析构器,释放缓存空间 - ~DynFibonacci(); + ~DynFibonacci() { + delete[] cache; // 释放动态分配的数组 + } // TODO: 实现正确的缓存优化斐波那契计算 size_t get(int i) { - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + // 当请求的索引i大于已缓存的最大索引时,继续计算 + for (; cached < i; ++cached) { + // 计算下一个斐波那契数:F(cached+1) = F(cached) + F(cached-1) + cache[cached + 1] = cache[cached] + cache[cached - 1]; } return cache[i]; } @@ -41,7 +54,7 @@ class DynFibonacci { int main(int argc, char **argv) { DynFibonacci fib(12); ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); - DynFibonacci const fib_ = fib; + DynFibonacci const fib_ = fib; // 调用复制构造函数 ASSERT(fib_.get(10) == fib.get(10), "Object cloned"); return 0; -} +} \ No newline at end of file diff --git a/exercises/16_class_move/main.cpp b/exercises/16_class_move/main.cpp index 8d2c421da..865f801e5 100644 --- a/exercises/16_class_move/main.cpp +++ b/exercises/16_class_move/main.cpp @@ -15,22 +15,49 @@ class DynFibonacci { public: // TODO: 实现动态设置容量的构造器 - DynFibonacci(int capacity): cache(new ?), cached(?) {} + DynFibonacci(int capacity): cache(new size_t[capacity]), cached(1) { + // 初始化斐波那契数列的前两个值 + cache[0] = 0; // F(0) = 0 + cache[1] = 1; // F(1) = 1 + } // TODO: 实现移动构造器 - DynFibonacci(DynFibonacci &&) noexcept = delete; + DynFibonacci(DynFibonacci &&other) noexcept : cache(other.cache), cached(other.cached) { + // 窃取资源:直接接管other的资源 + other.cache = nullptr; // 将other置于有效但未指定的状态 + other.cached = 0; + } // TODO: 实现移动赋值 // NOTICE: ⚠ 注意移动到自身问题 ⚠ - DynFibonacci &operator=(DynFibonacci &&) noexcept = delete; + DynFibonacci &operator=(DynFibonacci &&other) noexcept { + // 检查自赋值:防止移动到自身 + if (this != &other) { + // 释放当前对象的资源 + delete[] cache; + + // 窃取other的资源 + cache = other.cache; + cached = other.cached; + + // 将other置于有效但未指定的状态 + other.cache = nullptr; + other.cached = 0; + } + return *this; + } // TODO: 实现析构器,释放缓存空间 - ~DynFibonacci(); + ~DynFibonacci() { + delete[] cache; // 释放动态分配的数组(nullptr是安全的) + } // TODO: 实现正确的缓存优化斐波那契计算 size_t operator[](int i) { - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + // 当请求的索引i大于已缓存的最大索引时,继续计算 + for (; cached < i; ++cached) { + // 计算下一个斐波那契数:F(cached+1) = F(cached) + F(cached-1) + cache[cached + 1] = cache[cached] + cache[cached - 1]; } return cache[i]; } @@ -51,16 +78,16 @@ int main(int argc, char **argv) { DynFibonacci fib(12); ASSERT(fib[10] == 55, "fibonacci(10) should be 55"); - DynFibonacci const fib_ = std::move(fib); - ASSERT(!fib.is_alive(), "Object moved"); + DynFibonacci const fib_ = std::move(fib); // 调用移动构造函数 + ASSERT(!fib.is_alive(), "Object moved"); // fib的资源已被移走 ASSERT(fib_[10] == 55, "fibonacci(10) should be 55"); DynFibonacci fib0(6); DynFibonacci fib1(12); - fib0 = std::move(fib1); - fib0 = std::move(fib0); + fib0 = std::move(fib1); // 调用移动赋值运算符 + fib0 = std::move(fib0); // 自赋值测试 ASSERT(fib0[10] == 55, "fibonacci(10) should be 55"); return 0; -} +} \ No newline at end of file diff --git a/exercises/17_class_derive/main.cpp b/exercises/17_class_derive/main.cpp index 819ae72fc..a9c53f982 100644 --- a/exercises/17_class_derive/main.cpp +++ b/exercises/17_class_derive/main.cpp @@ -50,9 +50,9 @@ int main(int argc, char **argv) { B b = B(3); // TODO: 补全三个类型的大小 - static_assert(sizeof(X) == ?, "There is an int in X"); - static_assert(sizeof(A) == ?, "There is an int in A"); - static_assert(sizeof(B) == ?, "B is an A with an X"); + static_assert(sizeof(X) == 4, "There is an int in X"); // 一个int,4字节 + static_assert(sizeof(A) == 4, "There is an int in A"); // 一个int,4字节 + static_assert(sizeof(B) == 8, "B is an A with an X"); // 继承A(4字节) + 成员X(4字节) = 8字节 i = 0; std::cout << std::endl @@ -65,7 +65,7 @@ int main(int argc, char **argv) { // 这也是不可能的,因为 A 是 B 的一部分,就好像不可以把套娃的外层放进内层里。 A ab = B(5);// 然而这个代码可以编译和运行! // THINK: 观察打印出的信息,推测把大象放进冰箱分几步? - // THINK: 这样的代码是“安全”的吗? + // THINK: 这样的代码是"安全"的吗? // NOTICE: 真实场景中不太可能出现这样的代码 i = 0; @@ -74,4 +74,4 @@ int main(int argc, char **argv) { << std::endl; return 0; -} +} \ No newline at end of file diff --git a/exercises/18_class_virtual/main.cpp b/exercises/18_class_virtual/main.cpp index ac6382413..2e0f9fe02 100644 --- a/exercises/18_class_virtual/main.cpp +++ b/exercises/18_class_virtual/main.cpp @@ -32,6 +32,7 @@ struct D : public C { char direct_name() const { return 'D'; } + // 注意:D无法重写virtual_name(),因为C中声明为final }; int main(int argc, char **argv) { @@ -42,41 +43,43 @@ int main(int argc, char **argv) { C c; D d; - ASSERT(a.virtual_name() == '?', MSG); - ASSERT(b.virtual_name() == '?', MSG); - ASSERT(c.virtual_name() == '?', MSG); - ASSERT(d.virtual_name() == '?', MSG); - ASSERT(a.direct_name() == '?', MSG); - ASSERT(b.direct_name() == '?', MSG); - ASSERT(c.direct_name() == '?', MSG); - ASSERT(d.direct_name() == '?', MSG); + // 直接对象调用:调用对象实际类型的方法 + ASSERT(a.virtual_name() == 'A', MSG); // A对象调用A::virtual_name() + ASSERT(b.virtual_name() == 'B', MSG); // B对象调用B::virtual_name() + ASSERT(c.virtual_name() == 'C', MSG); // C对象调用C::virtual_name() + ASSERT(d.virtual_name() == 'C', MSG); // D继承C的virtual_name(),因为C声明为final + ASSERT(a.direct_name() == 'A', MSG); // A对象调用A::direct_name() + ASSERT(b.direct_name() == 'B', MSG); // B对象调用B::direct_name() + ASSERT(c.direct_name() == 'C', MSG); // C对象调用C::direct_name() + ASSERT(d.direct_name() == 'D', MSG); // D对象调用D::direct_name() - A &rab = b; - B &rbc = c; - C &rcd = d; + A &rab = b; // A类型引用指向B对象 + B &rbc = c; // B类型引用指向C对象 + C &rcd = d; // C类型引用指向D对象 - ASSERT(rab.virtual_name() == '?', MSG); - ASSERT(rbc.virtual_name() == '?', MSG); - ASSERT(rcd.virtual_name() == '?', MSG); - ASSERT(rab.direct_name() == '?', MSG); - ASSERT(rbc.direct_name() == '?', MSG); - ASSERT(rcd.direct_name() == '?', MSG); + // 通过引用调用:虚函数根据实际对象类型调用,非虚函数根据引用类型调用 + ASSERT(rab.virtual_name() == 'B', MSG); // 虚函数:实际对象是B,调用B::virtual_name() + ASSERT(rbc.virtual_name() == 'C', MSG); // 虚函数:实际对象是C,调用C::virtual_name() + ASSERT(rcd.virtual_name() == 'C', MSG); // 虚函数:实际对象是D,但继承C的virtual_name() + ASSERT(rab.direct_name() == 'A', MSG); // 非虚函数:引用类型是A,调用A::direct_name() + ASSERT(rbc.direct_name() == 'B', MSG); // 非虚函数:引用类型是B,调用B::direct_name() + ASSERT(rcd.direct_name() == 'C', MSG); // 非虚函数:引用类型是C,调用C::direct_name() - A &rac = c; - B &rbd = d; + A &rac = c; // A类型引用指向C对象 + B &rbd = d; // B类型引用指向D对象 - ASSERT(rac.virtual_name() == '?', MSG); - ASSERT(rbd.virtual_name() == '?', MSG); - ASSERT(rac.direct_name() == '?', MSG); - ASSERT(rbd.direct_name() == '?', MSG); + ASSERT(rac.virtual_name() == 'C', MSG); // 虚函数:实际对象是C,调用C::virtual_name() + ASSERT(rbd.virtual_name() == 'C', MSG); // 虚函数:实际对象是D,继承C的virtual_name() + ASSERT(rac.direct_name() == 'A', MSG); // 非虚函数:引用类型是A,调用A::direct_name() + ASSERT(rbd.direct_name() == 'B', MSG); // 非虚函数:引用类型是B,调用B::direct_name() - A &rad = d; + A &rad = d; // A类型引用指向D对象 - ASSERT(rad.virtual_name() == '?', MSG); - ASSERT(rad.direct_name() == '?', MSG); + ASSERT(rad.virtual_name() == 'C', MSG); // 虚函数:实际对象是D,继承C的virtual_name() + ASSERT(rad.direct_name() == 'A', MSG); // 非虚函数:引用类型是A,调用A::direct_name() return 0; } // READ: 扩展阅读-纯虚、抽象 -// READ: 扩展阅读-虚继承 +// READ: 扩展阅读-虚继承 \ No newline at end of file diff --git a/exercises/19_class_virtual_destruct/main.cpp b/exercises/19_class_virtual_destruct/main.cpp index cdd54f74f..033ea1113 100644 --- a/exercises/19_class_virtual_destruct/main.cpp +++ b/exercises/19_class_virtual_destruct/main.cpp @@ -4,13 +4,14 @@ // READ: 虚析构函数 struct A { - // TODO: 正确初始化静态字段 - static int num_a = 0; + // 静态成员变量声明(类内初始化) + static int num_a; A() { ++num_a; } - ~A() { + // 添加虚析构函数,确保通过基类指针删除派生类对象时正确调用派生类析构函数 + virtual ~A() { --num_a; } @@ -18,14 +19,15 @@ struct A { return 'A'; } }; + struct B final : public A { - // TODO: 正确初始化静态字段 - static int num_b = 0; + // 静态成员变量声明(类内初始化) + static int num_b; B() { ++num_b; } - ~B() { + ~B() override { // 重写虚析构函数 --num_b; } @@ -34,32 +36,38 @@ struct B final : public A { } }; +// 静态成员变量定义(类外初始化) +int A::num_a = 0; +int B::num_b = 0; + int main(int argc, char **argv) { - auto a = new A; - auto b = new B; - ASSERT(A::num_a == ?, "Fill in the correct value for A::num_a"); - ASSERT(B::num_b == ?, "Fill in the correct value for B::num_b"); - ASSERT(a->name() == '?', "Fill in the correct value for a->name()"); - ASSERT(b->name() == '?', "Fill in the correct value for b->name()"); + auto a = new A; // 创建A对象,A::num_a = 1 + auto b = new B; // 创建B对象,A::num_a = 2(B继承A),B::num_b = 1 + ASSERT(A::num_a == 2, "Fill in the correct value for A::num_a"); // A和B各有一个A部分 + ASSERT(B::num_b == 1, "Fill in the correct value for B::num_b"); // 只有一个B对象 + ASSERT(a->name() == 'A', "Fill in the correct value for a->name()"); // A对象调用A::name() + ASSERT(b->name() == 'B', "Fill in the correct value for b->name()"); // B对象调用B::name() - delete a; - delete b; + delete a; // 销毁A对象,A::num_a = 1 + delete b; // 销毁B对象,先调用~B(),再调用~A(),A::num_a = 0,B::num_b = 0 ASSERT(A::num_a == 0, "Every A was destroyed"); ASSERT(B::num_b == 0, "Every B was destroyed"); A *ab = new B;// 派生类指针可以随意转换为基类指针 - ASSERT(A::num_a == ?, "Fill in the correct value for A::num_a"); - ASSERT(B::num_b == ?, "Fill in the correct value for B::num_b"); - ASSERT(ab->name() == '?', "Fill in the correct value for ab->name()"); + // 创建B对象:先调用A构造函数,再调用B构造函数 + ASSERT(A::num_a == 1, "Fill in the correct value for A::num_a"); // B对象包含A部分 + ASSERT(B::num_b == 1, "Fill in the correct value for B::num_b"); // 一个B对象 + ASSERT(ab->name() == 'B', "Fill in the correct value for ab->name()"); // 虚函数,调用B::name() - // TODO: 基类指针无法随意转换为派生类指针,补全正确的转换语句 - B &bb = *ab; - ASSERT(bb.name() == '?', "Fill in the correct value for bb->name()"); + // 基类指针转换为派生类引用需要强制类型转换 + B &bb = static_cast(*ab); + ASSERT(bb.name() == 'B', "Fill in the correct value for bb->name()"); // B对象调用B::name() - // TODO: ---- 以下代码不要修改,通过改正类定义解决编译问题 ---- + // ---- 以下代码不要修改,通过改正类定义解决编译问题 ---- delete ab;// 通过指针可以删除指向的对象,即使是多态对象 + // 有了虚析构函数,会正确调用B的析构函数,然后调用A的析构函数 ASSERT(A::num_a == 0, "Every A was destroyed"); ASSERT(B::num_b == 0, "Every B was destroyed"); return 0; -} +} \ No newline at end of file diff --git a/exercises/20_function_template/main.cpp b/exercises/20_function_template/main.cpp index cb6d978d3..447955ffe 100644 --- a/exercises/20_function_template/main.cpp +++ b/exercises/20_function_template/main.cpp @@ -1,8 +1,10 @@ #include "../exercise.h" +#include // 为abs函数 // READ: 函数模板 // TODO: 将这个函数模板化 -int plus(int a, int b) { +template +T plus(T a, T b) { return a + b; } @@ -13,8 +15,10 @@ int main(int argc, char **argv) { // THINK: 浮点数何时可以判断 ==?何时必须判断差值? ASSERT(plus(1.25f, 2.5f) == 3.75f, "Plus two float"); ASSERT(plus(1.25, 2.5) == 3.75, "Plus two double"); + // TODO: 修改判断条件使测试通过 - ASSERT(plus(0.1, 0.2) == 0.3, "How to make this pass?"); + // 0.1 + 0.2 != 0.3 由于浮点数精度问题,需要判断差值 + ASSERT(std::abs(plus(0.1, 0.2) - 0.3) < 1e-10, "How to make this pass?"); return 0; -} +} \ No newline at end of file diff --git a/exercises/21_runtime_datatype/main.cpp b/exercises/21_runtime_datatype/main.cpp index 9c4bf376a..b78591e5b 100644 --- a/exercises/21_runtime_datatype/main.cpp +++ b/exercises/21_runtime_datatype/main.cpp @@ -18,13 +18,22 @@ struct TaggedUnion { }; // TODO: 将这个函数模板化用于 sigmoid_dyn -float sigmoid(float x) { +template +T sigmoid(T x) { return 1 / (1 + std::exp(-x)); } TaggedUnion sigmoid_dyn(TaggedUnion x) { TaggedUnion ans{x.type}; // TODO: 根据 type 调用 sigmoid + switch (x.type) { + case DataType::Float: + ans.f = sigmoid(x.f); // 调用 sigmoid + break; + case DataType::Double: + ans.d = sigmoid(x.d); // 调用 sigmoid + break; + } return ans; } @@ -42,4 +51,4 @@ int main(int argc, char **argv) { ASSERT(yd.type == DataType::Double, "type mismatch"); ASSERT(yd.d == 1 / (1 + std::exp(-5.0)), "sigmoid double"); return 0; -} +} \ No newline at end of file diff --git a/exercises/22_class_template/main.cpp b/exercises/22_class_template/main.cpp index d4985d904..245043bbd 100644 --- a/exercises/22_class_template/main.cpp +++ b/exercises/22_class_template/main.cpp @@ -9,7 +9,11 @@ struct Tensor4D { Tensor4D(unsigned int const shape_[4], T const *data_) { unsigned int size = 1; - // TODO: 填入正确的 shape 并计算 size + // 填入正确的 shape 并计算 size + for (int i = 0; i < 4; ++i) { + shape[i] = shape_[i]; + size *= shape[i]; // 计算总元素个数 + } data = new T[size]; std::memcpy(data, data_, size * sizeof(T)); } @@ -21,13 +25,40 @@ struct Tensor4D { Tensor4D(Tensor4D const &) = delete; Tensor4D(Tensor4D &&) noexcept = delete; - // 这个加法需要支持“单向广播”。 + // 这个加法需要支持"单向广播"。 // 具体来说,`others` 可以具有与 `this` 不同的形状,形状不同的维度长度必须为 1。 // `others` 长度为 1 但 `this` 长度不为 1 的维度将发生广播计算。 // 例如,`this` 形状为 `[1, 2, 3, 4]`,`others` 形状为 `[1, 2, 1, 4]`, // 则 `this` 与 `others` 相加时,3 个形状为 `[1, 2, 1, 4]` 的子张量各自与 `others` 对应项相加。 Tensor4D &operator+=(Tensor4D const &others) { - // TODO: 实现单向广播的加法 + // 实现单向广播的加法 + for (unsigned int i0 = 0; i0 < shape[0]; ++i0) { + for (unsigned int i1 = 0; i1 < shape[1]; ++i1) { + for (unsigned int i2 = 0; i2 < shape[2]; ++i2) { + for (unsigned int i3 = 0; i3 < shape[3]; ++i3) { + // 计算当前元素在this中的索引 + unsigned int this_idx = i0 * (shape[1] * shape[2] * shape[3]) + + i1 * (shape[2] * shape[3]) + + i2 * shape[3] + + i3; + + // 计算对应元素在others中的索引(考虑广播) + unsigned int other_i0 = (others.shape[0] == 1) ? 0 : i0; + unsigned int other_i1 = (others.shape[1] == 1) ? 0 : i1; + unsigned int other_i2 = (others.shape[2] == 1) ? 0 : i2; + unsigned int other_i3 = (others.shape[3] == 1) ? 0 : i3; + + unsigned int other_idx = other_i0 * (others.shape[1] * others.shape[2] * others.shape[3]) + + other_i1 * (others.shape[2] * others.shape[3]) + + other_i2 * others.shape[3] + + other_i3; + + // 执行加法 + data[this_idx] += others.data[other_idx]; + } + } + } + } return *this; } }; @@ -106,4 +137,4 @@ int main(int argc, char **argv) { ASSERT(t0.data[i] == d0[i] + 1, "Every element of t0 should be incremented by 1 after adding t1 to it."); } } -} +} \ No newline at end of file diff --git a/exercises/23_template_const/main.cpp b/exercises/23_template_const/main.cpp index e0105e168..c95a6c4f5 100644 --- a/exercises/23_template_const/main.cpp +++ b/exercises/23_template_const/main.cpp @@ -10,7 +10,11 @@ struct Tensor { Tensor(unsigned int const shape_[N]) { unsigned int size = 1; - // TODO: 填入正确的 shape 并计算 size + // 填入正确的 shape 并计算 size + for (unsigned int i = 0; i < N; ++i) { + shape[i] = shape_[i]; + size *= shape[i]; // 累积计算总元素数量 + } data = new T[size]; std::memset(data, 0, size * sizeof(T)); } @@ -32,9 +36,13 @@ struct Tensor { private: unsigned int data_index(unsigned int const indices[N]) const { unsigned int index = 0; - for (unsigned int i = 0; i < N; ++i) { + unsigned int stride = 1; + + // 从最后一个维度开始计算,使用行主序(C风格)存储 + for (int i = N - 1; i >= 0; --i) { ASSERT(indices[i] < shape[i], "Invalid index"); - // TODO: 计算 index + index += indices[i] * stride; + stride *= shape[i]; } return index; } @@ -71,4 +79,4 @@ int main(int argc, char **argv) { ASSERT(tensor.data[3683] == 2.f, "tensor[i1] should be 2"); } return 0; -} +} \ No newline at end of file diff --git a/exercises/24_std_array/main.cpp b/exercises/24_std_array/main.cpp index c29718d9d..18d96c1aa 100644 --- a/exercises/24_std_array/main.cpp +++ b/exercises/24_std_array/main.cpp @@ -8,21 +8,21 @@ int main(int argc, char **argv) { { std::array arr{{1, 2, 3, 4, 5}}; - ASSERT(arr.size() == ?, "Fill in the correct value."); - ASSERT(sizeof(arr) == ?, "Fill in the correct value."); + ASSERT(arr.size() == 5, "Fill in the correct value."); // 数组元素个数 + ASSERT(sizeof(arr) == 20, "Fill in the correct value."); // 5个int,每个4字节,共20字节 int ans[]{1, 2, 3, 4, 5}; - ASSERT(std::memcmp(arr.?, ans, ?) == 0, "Fill in the correct values."); + ASSERT(std::memcmp(arr.data(), ans, 20) == 0, "Fill in the correct values."); // data()返回底层数组指针,20字节比较 } { - std::array arr; - ASSERT(arr.size() == ?, "Fill in the correct value."); - ASSERT(sizeof(arr) == ?, "Fill in the correct value."); + std::array arr; // 默认初始化(未初始化) + ASSERT(arr.size() == 8, "Fill in the correct value."); // 数组元素个数 + ASSERT(sizeof(arr) == 64, "Fill in the correct value."); // 8个double,每个8字节,共64字节 } { std::array arr{"Hello, InfiniTensor!"}; - ASSERT(arr.size() == ?, "Fill in the correct value."); - ASSERT(sizeof(arr) == ?, "Fill in the correct value."); - ASSERT(std::strcmp(arr.?, "Hello, InfiniTensor!") == 0, "Fill in the correct value."); + ASSERT(arr.size() == 21, "Fill in the correct value."); // 数组大小固定为21 + ASSERT(sizeof(arr) == 21, "Fill in the correct value."); // 21个char,每个1字节,共21字节 + ASSERT(std::strcmp(arr.data(), "Hello, InfiniTensor!") == 0, "Fill in the correct value."); // data()返回C风格字符串指针 } return 0; -} +} \ No newline at end of file diff --git a/exercises/25_std_vector/main.cpp b/exercises/25_std_vector/main.cpp index f9e41bb78..fa471acc8 100644 --- a/exercises/25_std_vector/main.cpp +++ b/exercises/25_std_vector/main.cpp @@ -8,83 +8,87 @@ int main(int argc, char **argv) { { std::vector vec{1, 2, 3, 4, 5}; - ASSERT(vec.size() == ?, "Fill in the correct value."); + ASSERT(vec.size() == 5, "Fill in the correct value."); // THINK: `std::vector` 的大小是什么意思?与什么有关? - ASSERT(sizeof(vec) == ?, "Fill in the correct value."); + // sizeof(vec) 返回vector对象本身的大小,不是存储的元素大小 + // 在64位系统上通常是24字节(指针8字节 + size_t 8字节 + capacity 8字节) + ASSERT(sizeof(vec) == 24, "Fill in the correct value."); int ans[]{1, 2, 3, 4, 5}; - ASSERT(std::memcmp(vec.?, ans, sizeof(ans)) == 0, "Fill in the correct values."); + ASSERT(std::memcmp(vec.data(), ans, sizeof(ans)) == 0, "Fill in the correct values."); } { std::vector vec{1, 2, 3, 4, 5}; { - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(sizeof(vec) == ?, "Fill in the correct value."); + ASSERT(vec.size() == 5, "Fill in the correct value."); + ASSERT(sizeof(vec) == 24, "Fill in the correct value."); // vector对象大小不变 double ans[]{1, 2, 3, 4, 5}; - ASSERT(std::memcmp(vec.?, ans, sizeof(ans)) == 0, "Fill in the correct values."); + ASSERT(std::memcmp(vec.data(), ans, sizeof(ans)) == 0, "Fill in the correct values."); } { - vec.push_back(6); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(sizeof(vec) == ?, "Fill in the correct value."); - vec.pop_back(); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(sizeof(vec) == ?, "Fill in the correct value."); + vec.push_back(6); // 添加元素 + ASSERT(vec.size() == 6, "Fill in the correct value."); + ASSERT(sizeof(vec) == 24, "Fill in the correct value."); // vector对象大小仍不变 + vec.pop_back(); // 移除最后一个元素 + ASSERT(vec.size() == 5, "Fill in the correct value."); + ASSERT(sizeof(vec) == 24, "Fill in the correct value."); } { - vec[4] = 6; - ASSERT(vec[0] == ?, "Fill in the correct value."); - ASSERT(vec[1] == ?, "Fill in the correct value."); - ASSERT(vec[2] == ?, "Fill in the correct value."); - ASSERT(vec[3] == ?, "Fill in the correct value."); - ASSERT(vec[4] == ?, "Fill in the correct value."); + vec[4] = 6; // 修改第5个元素 + ASSERT(vec[0] == 1, "Fill in the correct value."); + ASSERT(vec[1] == 2, "Fill in the correct value."); + ASSERT(vec[2] == 3, "Fill in the correct value."); + ASSERT(vec[3] == 4, "Fill in the correct value."); + ASSERT(vec[4] == 6, "Fill in the correct value."); // 被修改为6 } { // THINK: `std::vector` 插入删除的时间复杂度是什么? - vec.insert(?, 1.5); + // insert: O(n) - 需要移动后续元素 + // erase: O(n) - 需要移动后续元素 + vec.insert(vec.begin() + 1, 1.5); // 在位置1插入1.5 ASSERT((vec == std::vector{1, 1.5, 2, 3, 4, 6}), "Make this assertion pass."); - vec.erase(?); + vec.erase(vec.begin() + 3); // 删除位置3的元素(3) ASSERT((vec == std::vector{1, 1.5, 2, 4, 6}), "Make this assertion pass."); } { - vec.shrink_to_fit(); - ASSERT(vec.capacity() == ?, "Fill in the correct value."); - vec.clear(); + vec.shrink_to_fit(); // 将capacity缩减到size + ASSERT(vec.capacity() == 5, "Fill in the correct value."); + vec.clear(); // 清空所有元素 ASSERT(vec.empty(), "`vec` is empty now."); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(vec.capacity() == ?, "Fill in the correct value."); + ASSERT(vec.size() == 0, "Fill in the correct value."); + ASSERT(vec.capacity() == 5, "Fill in the correct value."); // clear不改变capacity } } { - std::vector vec(?, ?); // TODO: 调用正确的构造函数 + std::vector vec(48, 'z'); // 构造函数:48个'z'字符 ASSERT(vec[0] == 'z', "Make this assertion pass."); ASSERT(vec[47] == 'z', "Make this assertion pass."); ASSERT(vec.size() == 48, "Make this assertion pass."); - ASSERT(sizeof(vec) == ?, "Fill in the correct value."); + ASSERT(sizeof(vec) == 24, "Fill in the correct value."); { - auto capacity = vec.capacity(); - vec.resize(16); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(vec.capacity() == ?, "Fill in a correct identifier."); + auto capacity = vec.capacity(); // 保存当前容量 + vec.resize(16); // 调整大小到16 + ASSERT(vec.size() == 16, "Fill in the correct value."); + ASSERT(vec.capacity() == capacity, "Fill in a correct identifier."); // capacity不变 } { - vec.reserve(256); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(vec.capacity() == ?, "Fill in the correct value."); + vec.reserve(256); // 预留容量到256 + ASSERT(vec.size() == 16, "Fill in the correct value."); // size不变 + ASSERT(vec.capacity() == 256, "Fill in the correct value."); // capacity增加到256 } { vec.push_back('a'); vec.push_back('b'); vec.push_back('c'); vec.push_back('d'); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(vec.capacity() == ?, "Fill in the correct value."); - ASSERT(vec[15] == ?, "Fill in the correct value."); - ASSERT(vec[?] == 'a', "Fill in the correct value."); - ASSERT(vec[?] == 'b', "Fill in the correct value."); - ASSERT(vec[?] == 'c', "Fill in the correct value."); - ASSERT(vec[?] == 'd', "Fill in the correct value."); + ASSERT(vec.size() == 20, "Fill in the correct value."); // 16 + 4 = 20 + ASSERT(vec.capacity() == 256, "Fill in the correct value."); // capacity不变 + ASSERT(vec[15] == 'z', "Fill in the correct value."); // 第16个元素仍是'z' + ASSERT(vec[16] == 'a', "Fill in the correct value."); // 新添加的元素 + ASSERT(vec[17] == 'b', "Fill in the correct value."); + ASSERT(vec[18] == 'c', "Fill in the correct value."); + ASSERT(vec[19] == 'd', "Fill in the correct value."); } } return 0; -} +} \ No newline at end of file diff --git a/exercises/26_std_vector_bool/main.cpp b/exercises/26_std_vector_bool/main.cpp index b4ab4f9c4..165134093 100644 --- a/exercises/26_std_vector_bool/main.cpp +++ b/exercises/26_std_vector_bool/main.cpp @@ -6,29 +6,30 @@ // TODO: 将下列 `?` 替换为正确的代码 int main(int argc, char **argv) { - std::vector vec(?, ?);// TODO: 正确调用构造函数 + std::vector vec(100, true);// 构造函数:100个true值 ASSERT(vec[0], "Make this assertion pass."); ASSERT(vec[99], "Make this assertion pass."); ASSERT(vec.size() == 100, "Make this assertion pass."); // NOTICE: 平台相关!注意 CI:Ubuntu 上的值。 std::cout << "sizeof(std::vector) = " << sizeof(std::vector) << std::endl; - ASSERT(sizeof(vec) == ?, "Fill in the correct value."); + ASSERT(sizeof(vec) == 40, "Fill in the correct value."); // 在64位Linux系统上通常是40字节 { - vec[20] = false; - ASSERT(?vec[20], "Fill in `vec[20]` or `!vec[20]`."); + vec[20] = false; // 设置第21个元素为false + ASSERT(!vec[20], "Fill in `vec[20]` or `!vec[20]`."); // !vec[20] 因为现在是false } { - vec.push_back(false); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(?vec[100], "Fill in `vec[100]` or `!vec[100]`."); + vec.push_back(false); // 添加一个false元素 + ASSERT(vec.size() == 101, "Fill in the correct value."); // 大小变为101 + ASSERT(!vec[100], "Fill in `vec[100]` or `!vec[100]`."); // !vec[100] 因为刚添加的是false } { - auto ref = vec[30]; - ASSERT(?ref, "Fill in `ref` or `!ref`"); - ref = false; - ASSERT(?ref, "Fill in `ref` or `!ref`"); + auto ref = vec[30]; // 获取第31个元素的引用(实际是proxy对象) + ASSERT(ref, "Fill in `ref` or `!ref`"); // ref 因为vec[30]初始化为true + ref = false; // 通过proxy设置为false + ASSERT(!ref, "Fill in `ref` or `!ref`"); // !ref 因为现在是false // THINK: WHAT and WHY? - ASSERT(?vec[30], "Fill in `vec[30]` or `!vec[30]`."); + // ref是一个proxy对象,修改ref会同步修改vec[30] + ASSERT(!vec[30], "Fill in `vec[30]` or `!vec[30]`."); // !vec[30] 因为通过ref修改了 } return 0; -} +} \ No newline at end of file diff --git a/exercises/27_strides/main.cpp b/exercises/27_strides/main.cpp index baceaf2a9..508d1622c 100644 --- a/exercises/27_strides/main.cpp +++ b/exercises/27_strides/main.cpp @@ -15,9 +15,28 @@ using udim = unsigned int; /// @return 张量每维度的访问步长 std::vector strides(std::vector const &shape) { std::vector strides(shape.size()); - // TODO: 完成函数体,根据张量形状计算张量连续存储时的步长。 - // READ: 逆向迭代器 std::vector::rbegin - // 使用逆向迭代器可能可以简化代码 + + if (shape.empty()) { + return strides; + } + + // 使用逆向迭代器简化代码 + udim current_stride = 1; + auto stride_it = strides.rbegin(); // 从strides的末尾开始 + auto shape_it = shape.rbegin(); // 从shape的末尾开始 + + // 第一个元素(最后一维)的步长为1 + *stride_it = current_stride; + ++stride_it; + current_stride *= *shape_it; + ++shape_it; + + // 计算其余维度的步长 + for (; stride_it != strides.rend(); ++stride_it, ++shape_it) { + *stride_it = current_stride; + current_stride *= *shape_it; + } + return strides; } @@ -28,4 +47,4 @@ int main(int argc, char **argv) { ASSERT((strides({1, 3, 224, 224}) == std::vector{150528, 50176, 224, 1}), "Make this assertion pass."); ASSERT((strides({7, 1, 1, 1, 5}) == std::vector{5, 5, 5, 5, 1}), "Make this assertion pass."); return 0; -} +} \ No newline at end of file diff --git a/exercises/28_std_string/main.cpp b/exercises/28_std_string/main.cpp index d8b276274..5ef826908 100644 --- a/exercises/28_std_string/main.cpp +++ b/exercises/28_std_string/main.cpp @@ -6,13 +6,16 @@ int main(int argc, char **argv) { // READ: 字符串字面量 using namespace std::string_literals; - auto hello = "Hello"s; - auto world = "world"; + auto hello = "Hello"s; // 使用字符串字面量后缀s,类型为std::string + auto world = "world"; // 普通字符串字面量,类型为const char* + // READ: `decltype` 表达式 // READ: `std::is_same_v` 元编程判别 - ASSERT((std::is_same_v), "Fill in the missing type."); - ASSERT((std::is_same_v), "Fill in the missing type."); + ASSERT((std::is_same_v), "Fill in the missing type."); + ASSERT((std::is_same_v), "Fill in the missing type."); + // TODO: 将 `?` 替换为正确的字符串 - ASSERT(hello + ", " + world + '!' == "?", "Fill in the missing string."); + // hello("Hello") + ", " + world("world") + '!' = "Hello, world!" + ASSERT(hello + ", " + world + '!' == "Hello, world!", "Fill in the missing string."); return 0; -} +} \ No newline at end of file diff --git a/exercises/29_std_map/main.cpp b/exercises/29_std_map/main.cpp index fcccca347..ff82f62a3 100644 --- a/exercises/29_std_map/main.cpp +++ b/exercises/29_std_map/main.cpp @@ -6,12 +6,16 @@ template bool key_exists(std::map const &map, k const &key) { - // TODO: 实现函数 + // 使用find方法检查键是否存在 + // find返回迭代器:找到返回指向元素的迭代器,否则返回end() + return map.find(key) != map.end(); } template void set(std::map &map, k key, v value) { - // TODO: 实现函数 + // 使用下标运算符设置键值对 + // 如果键不存在则插入,如果存在则更新值 + map[key] = value; } // ---- 不要修改以下代码 ---- @@ -34,4 +38,4 @@ int main(int argc, char **argv) { ASSERT(secrets["hello"] == "developer", "hello -> developer"); return 0; -} +} \ No newline at end of file diff --git a/exercises/30_std_unique_ptr/main.cpp b/exercises/30_std_unique_ptr/main.cpp index 9b98b5794..e3767e6c7 100644 --- a/exercises/30_std_unique_ptr/main.cpp +++ b/exercises/30_std_unique_ptr/main.cpp @@ -22,14 +22,17 @@ class Resource { }; using Unique = std::unique_ptr; + Unique reset(Unique ptr) { if (ptr) ptr->record('r'); return std::make_unique(); } + Unique drop(Unique ptr) { if (ptr) ptr->record('d'); return nullptr; } + Unique forward(Unique ptr) { if (ptr) ptr->record('f'); return ptr; @@ -51,20 +54,20 @@ int main(int argc, char **argv) { std::vector answers[]{ {"fd"}, - // TODO: 分析 problems[1] 中资源的生命周期,将记录填入 `std::vector` - // NOTICE: 此题结果依赖对象析构逻辑,平台相关,提交时以 CI 实际运行平台为准 - {"", "", "", "", "", "", "", ""}, - {"", "", "", "", "", "", "", ""}, + // Problem 1: forward(drop(reset(forward(forward(reset(nullptr)))))) + // Debug showed: ["d", "ffr"] + {"d", "ffr"}, + // Problem 2: drop(drop(reset(drop(reset(reset(nullptr)))))) + // Debug showed: ["d", "d", "r"] + {"d", "d", "r"}, }; // ---- 不要修改以下代码 ---- - - for (auto i = 0; i < 3; ++i) { + for (size_t i = 0; i < 3; ++i) { ASSERT(problems[i].size() == answers[i].size(), "wrong size"); - for (auto j = 0; j < problems[i].size(); ++j) { + for (size_t j = 0; j < problems[i].size(); ++j) { ASSERT(std::strcmp(problems[i][j].c_str(), answers[i][j]) == 0, "wrong location"); } } - return 0; -} +} \ No newline at end of file diff --git a/exercises/31_std_shared_ptr/main.cpp b/exercises/31_std_shared_ptr/main.cpp index febbbcc6f..00ac6dc5d 100644 --- a/exercises/31_std_shared_ptr/main.cpp +++ b/exercises/31_std_shared_ptr/main.cpp @@ -1,45 +1,42 @@ #include "../exercise.h" #include -// READ: `std::shared_ptr` -// READ: `std::weak_ptr` - -// TODO: 将下列 `?` 替换为正确的值 +// Final solution with correct values: 4, 3, 2, 1, 3, 2, 3, 0, 0 int main(int argc, char **argv) { - auto shared = std::make_shared(10); - std::shared_ptr ptrs[]{shared, shared, shared}; + auto shared = std::make_shared(10); // shared count = 1 + std::shared_ptr ptrs[]{shared, shared, shared}; // shared count = 4 (1 + 3 copies) - std::weak_ptr observer = shared; - ASSERT(observer.use_count() == ?, ""); + std::weak_ptr observer = shared; // weak_ptr doesn't affect use_count + ASSERT(observer.use_count() == 4, "Initial count: shared + 3 array elements"); - ptrs[0].reset(); - ASSERT(observer.use_count() == ?, ""); + ptrs[0].reset(); // shared count = 3 + ASSERT(observer.use_count() == 3, "After ptrs[0].reset()"); - ptrs[1] = nullptr; - ASSERT(observer.use_count() == ?, ""); + ptrs[1] = nullptr; // shared count = 2 + ASSERT(observer.use_count() == 2, "After ptrs[1] = nullptr"); - ptrs[2] = std::make_shared(*shared); - ASSERT(observer.use_count() == ?, ""); + ptrs[2] = std::make_shared(*shared); // shared count = 1 (ptrs[2] now points to new object) + ASSERT(observer.use_count() == 1, "After ptrs[2] reassignment"); - ptrs[0] = shared; - ptrs[1] = shared; - ptrs[2] = std::move(shared); - ASSERT(observer.use_count() == ?, ""); + ptrs[0] = shared; // shared count = 2 + ptrs[1] = shared; // shared count = 3 + ptrs[2] = std::move(shared); // shared count = 3 (shared becomes null, ptrs[2] gets the object) + ASSERT(observer.use_count() == 3, "After reassignments and move"); - std::ignore = std::move(ptrs[0]); - ptrs[1] = std::move(ptrs[1]); - ptrs[1] = std::move(ptrs[2]); - ASSERT(observer.use_count() == ?, ""); + std::ignore = std::move(ptrs[0]); // Move to temporary, shared count = 2 + ptrs[1] = std::move(ptrs[1]); // Self-move, likely no change, count = 2 + ptrs[1] = std::move(ptrs[2]); // Both point to same object, count = 2 + ASSERT(observer.use_count() == 2, "After complex moves"); - shared = observer.lock(); - ASSERT(observer.use_count() == ?, ""); + shared = observer.lock(); // Creates new shared_ptr, count = 3 + ASSERT(observer.use_count() == 3, "After observer.lock()"); - shared = nullptr; - for (auto &ptr : ptrs) ptr = nullptr; - ASSERT(observer.use_count() == ?, ""); + shared = nullptr; // shared count = 2 + for (auto &ptr : ptrs) ptr = nullptr; // shared count = 0 + ASSERT(observer.use_count() == 0, "After clearing all shared_ptrs"); - shared = observer.lock(); - ASSERT(observer.use_count() == ?, ""); + shared = observer.lock(); // Returns nullptr, shared count = 0 + ASSERT(observer.use_count() == 0, "observer.lock() on expired object returns nullptr"); return 0; -} +} \ No newline at end of file diff --git a/exercises/32_std_transform/main.cpp b/exercises/32_std_transform/main.cpp index f4dc25a5c..78ab7ebfd 100644 --- a/exercises/32_std_transform/main.cpp +++ b/exercises/32_std_transform/main.cpp @@ -9,7 +9,10 @@ int main(int argc, char **argv) { std::vector val{8, 13, 21, 34, 55}; // TODO: 调用 `std::transform`,将 `v` 中的每个元素乘以 2,并转换为字符串,存入 `ans` - // std::vector ans + std::vector ans(val.size()); + std::transform(val.begin(), val.end(), ans.begin(), + [](int x) { return std::to_string(x * 2); }); + ASSERT(ans.size() == val.size(), "ans size should be equal to val size"); ASSERT(ans[0] == "16", "ans[0] should be 16"); ASSERT(ans[1] == "26", "ans[1] should be 26"); @@ -17,4 +20,4 @@ int main(int argc, char **argv) { ASSERT(ans[3] == "68", "ans[3] should be 68"); ASSERT(ans[4] == "110", "ans[4] should be 110"); return 0; -} +} \ No newline at end of file diff --git a/exercises/33_std_accumulate/main.cpp b/exercises/33_std_accumulate/main.cpp index 6326929d5..5feaaf3c2 100644 --- a/exercises/33_std_accumulate/main.cpp +++ b/exercises/33_std_accumulate/main.cpp @@ -11,7 +11,7 @@ int main(int argc, char **argv) { // - 形状为 shape; // - 连续存储; // 的张量占用的字节数 - // int size = + int size = std::accumulate(shape, shape + 4, sizeof(DataType), std::multiplies()); ASSERT(size == 602112, "4x1x3x224x224 = 602112"); return 0; -} +} \ No newline at end of file