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..f96b1ee3b 100644 --- a/exercises/01_variable&add/main.cpp +++ b/exercises/01_variable&add/main.cpp @@ -1,10 +1,14 @@ #include "../exercise.h" +#include // READ: 运算符 int main(int argc, char **argv) { - // TODO: 补全变量定义并打印加法运算 - // x ? + // 定义并初始化变量 x + int x = 10;// 可以改成任何你喜欢的数字 + + // 打印加法运算 std::cout << x << " + " << x << " = " << x + x << std::endl; + return 0; -} +} \ No newline at end of file diff --git a/exercises/02_function/main.cpp b/exercises/02_function/main.cpp index b5eef7f28..f423748f7 100644 --- a/exercises/02_function/main.cpp +++ b/exercises/02_function/main.cpp @@ -1,10 +1,12 @@ #include "../exercise.h" +#include // READ: 声明 // NOTICE: cppreference 中的示例中指出了复杂声明的解读法,建议认真阅读。 // 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 +18,5 @@ int main(int argc, char **argv) { int add(int a, int b) { // TODO: 补全函数定义,但不要移动代码行 + return a + b;// 返回两个参数的和 } diff --git a/exercises/03_argument¶meter/main.cpp b/exercises/03_argument¶meter/main.cpp index 7fb5d3c2f..b4d140871 100644 --- a/exercises/03_argument¶meter/main.cpp +++ b/exercises/03_argument¶meter/main.cpp @@ -1,4 +1,5 @@ #include "../exercise.h" +#include // READ: // THINK: 参数都有哪些传递方式?如何选择传递方式? @@ -8,19 +9,19 @@ void func(int); // TODO: 为下列 ASSERT 填写正确的值 int main(int argc, char **argv) { auto arg = 99; - ASSERT(arg == ?, "arg should be ?"); - std::cout << "befor func call: " << arg << std::endl; + ASSERT(arg == 99, "arg should be 99"); + std::cout << "before 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 ?"); - std::cout << "befor add: " << param << std::endl; + ASSERT(param == 99, "param should be 99"); + std::cout << "before 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..f9ce40c4f 100644 --- a/exercises/04_static/main.cpp +++ b/exercises/04_static/main.cpp @@ -1,4 +1,5 @@ #include "../exercise.h" +#include // READ: `static` 关键字 // THINK: 这个函数的两个 `static` 各自的作用是什么? @@ -10,10 +11,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..1ef359394 100644 --- a/exercises/05_constexpr/main.cpp +++ b/exercises/05_constexpr/main.cpp @@ -1,14 +1,20 @@ #include "../exercise.h" +// 修改后的斐波那契函数:使用迭代(动态规划) constexpr unsigned long long fibonacci(int i) { - switch (i) { - case 0: - return 0; - case 1: - return 1; - default: - return fibonacci(i - 1) + fibonacci(i - 2); + if (i <= 0) return 0; + if (i == 1) return 1; + + unsigned long long prev = 0;// F(0) + unsigned long long curr = 1;// F(1) + + for (int n = 2; n <= i; ++n) { + unsigned long long next = prev + curr; + prev = curr; + curr = next; } + + return curr; } int main(int argc, char **argv) { @@ -16,11 +22,9 @@ int main(int argc, char **argv) { 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); + const 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..5b9e8159e 100644 --- a/exercises/06_array/main.cpp +++ b/exercises/06_array/main.cpp @@ -11,16 +11,16 @@ unsigned long long fibonacci(int i) { return 1; default: // TODO: 补全三目表达式缺失的部分 - return ? : (arr[i] = fibonacci(i - 1) + fibonacci(i - 2)); + return arr[i] ? 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"); + 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..6c5385877 100644 --- a/exercises/07_loop/main.cpp +++ b/exercises/07_loop/main.cpp @@ -5,10 +5,13 @@ // READ: 纯函数 static unsigned long long fibonacci(int i) { // TODO: 为缓存设置正确的初始值 - static unsigned long long cache[96], cached; + static unsigned long long cache[96] = {0, 1}; + static unsigned long long cached = 1;// 已经缓存的最大索引 + // TODO: 设置正确的循环条件 - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + // 如果i比已经缓存的最大索引大,就继续计算并缓存 + for (; cached < i; ++cached) { + cache[cached + 1] = cache[cached] + cache[cached - 1]; } return cache[i]; } diff --git a/exercises/08_pointer/main.cpp b/exercises/08_pointer/main.cpp index ba37173f5..4b15ed627 100644 --- a/exercises/08_pointer/main.cpp +++ b/exercises/08_pointer/main.cpp @@ -3,8 +3,14 @@ // 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] + + // 检查从 ptr 开始,每隔 stride 个元素的 len 个数是否符合斐波那契递推关系 + for (int i = 0; i < len - 2; ++i) { + // arr[i+2] = arr[i] + arr[i+1] + if (ptr[(i + 2) * stride] != ptr[i * stride] + ptr[(i + 1) * stride]) { + return false; + } + } return true; } diff --git a/exercises/09_enum&union/main.cpp b/exercises/09_enum&union/main.cpp index 3f2cec768..914317959 100644 --- a/exercises/09_enum&union/main.cpp +++ b/exercises/09_enum&union/main.cpp @@ -37,8 +37,8 @@ ColorEnum convert_by_pun(Color c) { TypePun pun; // TODO: 补全类型双关转换 - - return pun.e; + pun.c = c; // 将 Color 值赋给 union 的 c 成员 + return pun.e;// 通过 e 成员读取 ColorEnum 值 } int main(int argc, char **argv) { @@ -47,4 +47,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..be6a55c5b 100644 --- a/exercises/10_trivial/main.cpp +++ b/exercises/10_trivial/main.cpp @@ -1,26 +1,38 @@ #include "../exercise.h" -// READ: Trivial type +// READ: 枚举类型 -struct FibonacciCache { - unsigned long long cache[16]; - int cached; +// `enum` 是 C 的兼容类型,本质上其对应类型的常量。 +// 在 `enum` 中定义标识符等价于定义 constexpr 常量, +// 这些标识符不需要前缀,可以直接引用。 +// 因此 `enum` 定义会污染命名空间。 +enum ColorEnum : unsigned char { + COLOR_RED = 31, + COLOR_GREEN, + COLOR_YELLOW, + COLOR_BLUE, }; -// TODO: 实现正确的缓存优化斐波那契计算 -static unsigned long long fibonacci(FibonacciCache &cache, int i) { - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; - } - return cache.cache[i]; +// 有作用域枚举型是 C++ 引入的类型安全枚举。 +// 其内部标识符需要带前缀引用,如 `Color::Red`。 +// 作用域枚举型可以避免命名空间污染,并提供类型安全保证。 +enum class Color : int { + Red = COLOR_RED, + Green, + Yellow, + Blue, +}; + +ColorEnum convert_by_pun(Color c) { + // 正确的转换方式:使用 static_cast + // 首先将作用域枚举转换为底层类型 int,然后再转换为 ColorEnum + return static_cast(static_cast(c)); } int main(int argc, char **argv) { - // TODO: 初始化缓存结构体,使计算正确 - // NOTICE: C/C++ 中,读取未初始化的变量(包括结构体变量)是未定义行为 - // READ: 初始化的各种写法 - FibonacciCache fib; - ASSERT(fibonacci(fib, 10) == 55, "fibonacci(10) should be 55"); - std::cout << "fibonacci(10) = " << fibonacci(fib, 10) << std::endl; + ASSERT(convert_by_pun(Color::Red) == COLOR_RED, "Type punning conversion"); + ASSERT(convert_by_pun(Color::Green) == COLOR_GREEN, "Type punning conversion"); + 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/11_method/main.cpp b/exercises/11_method/main.cpp index 0e08e0a36..5554824ba 100644 --- a/exercises/11_method/main.cpp +++ b/exercises/11_method/main.cpp @@ -1,4 +1,5 @@ #include "../exercise.h" +#include struct Fibonacci { unsigned long long cache[128]; @@ -6,17 +7,63 @@ struct Fibonacci { // TODO: 实现正确的缓存优化斐波那契计算 unsigned long long get(int i) { - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + // 如果请求的索引超出了当前缓存的范围,扩展缓存 + if (i >= cached) { + // 确保至少有两个基础值来计算 + if (cached < 2) { + // 如果缓存为空,初始化前两个值 + if (cached == 0) { + cache[0] = 0;// F(0) + cached = 1; + } + if (cached == 1) { + cache[1] = 1;// F(1) + cached = 2; + } + } + + // 计算并缓存从当前缓存位置到i的斐波那契数 + for (int j = cached; j <= i; ++j) { + cache[j] = cache[j - 1] + cache[j - 2]; + ++cached; + } } + + // 返回缓存中的值 return cache[i]; } }; int main(int argc, char **argv) { // TODO: 初始化缓存结构体,使计算正确 - Fibonacci fib; + // 使用聚合初始化:第一个花括号初始化cache数组,第二个值初始化cached + Fibonacci fib = {{0, 1}, 2};// 直接初始化前两个值和cached计数器 + + // 或者使用值初始化,然后在get函数中动态计算 + // Fibonacci fib = {}; // 所有成员初始化为0,get函数会处理初始化 + + // 验证斐波那契数列 + std::cout << "F(0) = " << fib.get(0) << std::endl; // 0 + std::cout << "F(1) = " << fib.get(1) << std::endl; // 1 + std::cout << "F(2) = " << fib.get(2) << std::endl; // 1 + std::cout << "F(3) = " << fib.get(3) << std::endl; // 2 + std::cout << "F(4) = " << fib.get(4) << std::endl; // 3 + std::cout << "F(5) = " << fib.get(5) << std::endl; // 5 + std::cout << "F(6) = " << fib.get(6) << std::endl; // 8 + std::cout << "F(7) = " << fib.get(7) << std::endl; // 13 + std::cout << "F(8) = " << fib.get(8) << std::endl; // 21 + std::cout << "F(9) = " << fib.get(9) << std::endl; // 34 + std::cout << "F(10) = " << fib.get(10) << std::endl;// 55 + + // 测试断言 ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; + + // 测试缓存效果:多次获取应该从缓存直接返回 + std::cout << "\nTesting cache performance:" << std::endl; + std::cout << "F(15) = " << fib.get(15) << std::endl;// 610 + std::cout << "F(20) = " << fib.get(20) << std::endl;// 6765 + std::cout << "F(10) = " << fib.get(10) << std::endl;// 55 - 应该从缓存直接返回 + 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..468c2e90f 100644 --- a/exercises/12_method_const/main.cpp +++ b/exercises/12_method_const/main.cpp @@ -1,17 +1,37 @@ #include "../exercise.h" +#include // READ: 有 cv 限定符的成员函数 struct Fibonacci { int numbers[11]; + // TODO: 修改方法签名和实现,使测试通过 - int get(int i) { + // 需要添加 const 限定符,因为这个方法会在 const 对象上调用 + int get(int i) const { + // 检查索引是否在有效范围内 + if (i >= 0 && i < 11) { + return numbers[i]; + } + // 如果索引越界,返回 -1 作为错误值 + return -1; } }; int main(int argc, char **argv) { + // 使用 constexpr 创建常量对象 + // 注意:这里需要双花括号进行聚合初始化 Fibonacci constexpr FIB{{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55}}; + + // 测试基本的斐波那契值 ASSERT(FIB.get(10) == 55, "fibonacci(10) should be 55"); std::cout << "fibonacci(10) = " << FIB.get(10) << std::endl; + + // 测试其他值 + std::cout << "fibonacci(0) = " << FIB.get(0) << std::endl; + std::cout << "fibonacci(1) = " << FIB.get(1) << std::endl; + std::cout << "fibonacci(2) = " << FIB.get(2) << std::endl; + std::cout << "fibonacci(5) = " << FIB.get(5) << 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..a96cef9ab 100644 --- a/exercises/13_class/main.cpp +++ b/exercises/13_class/main.cpp @@ -13,14 +13,34 @@ class Fibonacci { int cached; public: - // TODO: 实现构造器 - // Fibonacci() + // 实现构造器 + // 初始化缓存数组,设置前两个斐波那契数(F(0)=0, F(1)=1) + Fibonacci() { + cache[0] = 0;// F(0) + cache[1] = 1;// F(1) + cached = 1; // 目前已经缓存到索引1 + } - // TODO: 实现正确的缓存优化斐波那契计算 + // 实现正确的缓存优化斐波那契计算 size_t get(int i) { - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + // 如果请求的索引超出了数组大小,直接返回0(或可以抛出异常,但这里简单处理) + if (i >= 16 || i < 0) { + return 0; + } + + // 如果请求的索引已经计算过,直接从缓存返回 + if (i <= cached) { + return cache[i]; + } + + // 计算从 cached+1 到 i 的所有斐波那契数 + for (int j = cached + 1; j <= i; ++j) { + cache[j] = cache[j - 1] + cache[j - 2]; } + + // 更新已缓存的最大索引 + cached = i; + return cache[i]; } }; @@ -31,5 +51,19 @@ int main(int argc, char **argv) { Fibonacci fib; ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; + + // 可以添加更多测试 + std::cout << "fibonacci(0) = " << fib.get(0) << std::endl; + std::cout << "fibonacci(1) = " << fib.get(1) << std::endl; + std::cout << "fibonacci(2) = " << fib.get(2) << std::endl; + std::cout << "fibonacci(3) = " << fib.get(3) << std::endl; + std::cout << "fibonacci(4) = " << fib.get(4) << std::endl; + std::cout << "fibonacci(5) = " << fib.get(5) << std::endl; + std::cout << "fibonacci(6) = " << fib.get(6) << std::endl; + std::cout << "fibonacci(7) = " << fib.get(7) << std::endl; + std::cout << "fibonacci(8) = " << fib.get(8) << std::endl; + std::cout << "fibonacci(9) = " << fib.get(9) << std::endl; + 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..be76c7e62 100644 --- a/exercises/14_class_destruct/main.cpp +++ b/exercises/14_class_destruct/main.cpp @@ -1,4 +1,5 @@ #include "../exercise.h" +#include // 用于异常处理 // READ: 析构函数 // READ: RAII @@ -8,26 +9,92 @@ class DynFibonacci { size_t *cache; int cached; + int capacity;// 记录缓存容量 public: - // TODO: 实现动态设置容量的构造器 - DynFibonacci(int capacity): cache(new ?), cached(?) {} + // 实现动态设置容量的构造器 + // 分配缓存空间,初始化前两个斐波那契数 + DynFibonacci(int cap) : capacity(cap), cached(1) { + // 检查容量是否足够(至少需要2个位置存储F(0)和F(1)) + if (cap < 2) { + throw std::invalid_argument("Capacity must be at least 2"); + } + + // 动态分配缓存数组 + cache = new size_t[capacity]; - // TODO: 实现析构器,释放缓存空间 - ~DynFibonacci(); + // 初始化前两个斐波那契数 + cache[0] = 0;// F(0) + cache[1] = 1;// F(1) + } + + // 实现析构器,释放缓存空间 + ~DynFibonacci() { + delete[] cache; + } - // TODO: 实现正确的缓存优化斐波那契计算 + // 实现正确的缓存优化斐波那契计算 size_t get(int i) { - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + // 边界检查 + if (i < 0) { + throw std::out_of_range("Index cannot be negative"); + } + + if (i >= capacity) { + throw std::out_of_range("Index exceeds cache capacity"); + } + + // 如果请求的索引已经计算过,直接从缓存返回 + if (i <= cached) { + return cache[i]; + } + + // 计算从 cached+1 到 i 的所有斐波那契数 + for (int j = cached + 1; j <= i; ++j) { + cache[j] = cache[j - 1] + cache[j - 2]; } + + // 更新已缓存的最大索引 + cached = i; + return cache[i]; } -}; + + // 禁止拷贝构造函数和拷贝赋值(因为管理动态内存) + // 防止浅拷贝导致重复释放内存 + DynFibonacci(const DynFibonacci &) = delete; + DynFibonacci &operator=(const DynFibonacci &) = delete; + + // 可以提供移动构造函数和移动赋值运算符(可选) + // DynFibonacci(DynFibonacci&& other) noexcept + // : cache(other.cache), cached(other.cached), capacity(other.capacity) { + // other.cache = nullptr; + // other.cached = 0; + // other.capacity = 0; + // } +};// 注意:这里需要类定义结束 int main(int argc, char **argv) { - DynFibonacci fib(12); - ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); - std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; + try { + DynFibonacci fib(12); + ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); + std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; + + // 测试边界情况 + std::cout << "fibonacci(0) = " << fib.get(0) << std::endl; + std::cout << "fibonacci(1) = " << fib.get(1) << std::endl; + std::cout << "fibonacci(2) = " << fib.get(2) << std::endl; + std::cout << "fibonacci(11) = " << fib.get(11) << std::endl; + + // 测试异常情况(取消注释可以测试) + // DynFibonacci small_fib(1); // 应该抛出异常 + // std::cout << fib.get(12) << std::endl; // 应该抛出异常 + // std::cout << fib.get(-1) << std::endl; // 应该抛出异常 + + } catch (const std::exception &e) { + std::cerr << "Error: " << e.what() << std::endl; + return 1; + } + 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..e8e6af520 100644 --- a/exercises/15_class_clone/main.cpp +++ b/exercises/15_class_clone/main.cpp @@ -1,4 +1,6 @@ #include "../exercise.h" +#include // 用于std::copy +#include // 用于异常处理 // READ: 复制构造函数 // READ: 函数定义(显式弃置) @@ -7,22 +9,62 @@ class DynFibonacci { size_t *cache; int cached; + int capacity;// 记录缓存容量 public: - // TODO: 实现动态设置容量的构造器 - DynFibonacci(int capacity): cache(new ?), cached(?) {} + // 实现动态设置容量的构造器 + DynFibonacci(int capacity) : capacity(capacity), + cached(1), + cache(new size_t[capacity]) { + // 检查容量是否足够 + if (capacity < 2) { + delete[] cache;// 释放已分配的内存 + throw std::invalid_argument("Capacity must be at least 2"); + } + + // 初始化前两个斐波那契数 + cache[0] = 0;// F(0) + cache[1] = 1;// F(1) + } - // TODO: 实现复制构造器 - DynFibonacci(DynFibonacci const &) = delete; + // 实现复制构造器 - 深拷贝 + // 不能删除,因为main函数中使用了复制 + DynFibonacci(DynFibonacci const &other) : capacity(other.capacity), + cached(other.cached), + cache(new size_t[other.capacity]) { + // 复制缓存数组中的所有元素 + std::copy(other.cache, other.cache + other.capacity, cache); + } - // TODO: 实现析构器,释放缓存空间 - ~DynFibonacci(); + // 实现析构器,释放缓存空间 + ~DynFibonacci() { + delete[] cache; + } - // TODO: 实现正确的缓存优化斐波那契计算 + // 实现正确的缓存优化斐波那契计算 - 非常量版本 size_t get(int i) { - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + // 边界检查 + if (i < 0) { + throw std::out_of_range("Index cannot be negative"); + } + + if (i >= capacity) { + throw std::out_of_range("Index exceeds cache capacity"); + } + + // 如果请求的索引已经计算过,直接从缓存返回 + if (i <= cached) { + return cache[i]; } + + // 计算从 cached+1 到 i 的所有斐波那契数 + for (int j = cached + 1; j <= i; ++j) { + cache[j] = cache[j - 1] + cache[j - 2]; + } + + // 更新已缓存的最大索引 + cached = i; + return cache[i]; } @@ -30,18 +72,52 @@ class DynFibonacci { // NOTICE: 名字相同参数也相同,但 const 修饰不同的方法是一对重载方法,可以同时存在 // 本质上,方法是隐藏了 this 参数的函数 // const 修饰作用在 this 上,因此它们实际上参数不同 + // 常量版本 - 只能读取已缓存的值 size_t get(int i) const { if (i <= cached) { return cache[i]; } ASSERT(false, "i out of range"); + return 0;// 为了避免编译器警告,虽然ASSERT会终止程序 } + + // 可以添加赋值运算符,但这里不是必需的 + // DynFibonacci& operator=(const DynFibonacci& other) { + // if (this != &other) { + // delete[] cache; + // capacity = other.capacity; + // cached = other.cached; + // cache = new size_t[capacity]; + // std::copy(other.cache, other.cache + capacity, cache); + // } + // return *this; + // } }; int main(int argc, char **argv) { DynFibonacci fib(12); ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); - DynFibonacci const fib_ = fib; + + // 复制构造 - 这会调用复制构造函数 + // 由于fib已经计算到10,所以缓存中有F(0)到F(10)的值 + DynFibonacci const fib_ = fib;// 复制构造函数调用 + + // 验证复制是否成功 ASSERT(fib_.get(10) == fib.get(10), "Object cloned"); + + // 验证常量版本的方法 + std::cout << "Original fibonacci(10) = " << fib.get(10) << std::endl; + std::cout << "Copied fibonacci(10) = " << fib_.get(10) << std::endl; + + // 测试常量版本的边界情况 + // 下面的代码会触发ASSERT,因为常量对象不能计算新的值 + // DynFibonacci const const_fib(5); + // std::cout << const_fib.get(3) << std::endl; // 可以,因为3 <= 1? 不,实际上cached=1,所以会触发ASSERT + // 需要先计算值 + DynFibonacci non_const_fib(5); + non_const_fib.get(3); // 计算F(3) + DynFibonacci const const_fib2 = non_const_fib; // 复制已计算的缓存 + std::cout << "Const fibonacci(3) = " << const_fib2.get(3) << std::endl;// 可以,因为已经缓存了 + 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..60ad768ca 100644 --- a/exercises/16_class_move/main.cpp +++ b/exercises/16_class_move/main.cpp @@ -1,4 +1,6 @@ #include "../exercise.h" +#include +#include // READ: 左值右值(概念) // READ: 左值右值(细节) @@ -12,26 +14,77 @@ class DynFibonacci { size_t *cache; int cached; + int capacity; public: - // TODO: 实现动态设置容量的构造器 - DynFibonacci(int capacity): cache(new ?), cached(?) {} + // 实现动态设置容量的构造器 + DynFibonacci(int capacity) : capacity(capacity), + cached(1), + cache(new size_t[capacity]) { + if (capacity < 2) { + delete[] cache; + throw std::invalid_argument("Capacity must be at least 2"); + } + + cache[0] = 0; + cache[1] = 1; + } - // TODO: 实现移动构造器 - DynFibonacci(DynFibonacci &&) noexcept = delete; + // 实现移动构造器 + DynFibonacci(DynFibonacci &&other) noexcept + : cache(other.cache), cached(other.cached), capacity(other.capacity) { + // 将被移动对象的资源指针置空,确保其析构时不会删除资源 + other.cache = nullptr; + other.cached = 0; + other.capacity = 0; + } + + // 实现移动赋值 + // 注意移动到自身问题 + DynFibonacci &operator=(DynFibonacci &&other) noexcept { + // 检查自赋值:如果地址相同,则什么都不做 + if (this != &other) { + // 释放当前对象的资源 + delete[] cache; - // TODO: 实现移动赋值 - // NOTICE: ⚠ 注意移动到自身问题 ⚠ - DynFibonacci &operator=(DynFibonacci &&) noexcept = delete; + // 接管其他对象的资源 + cache = other.cache; + cached = other.cached; + capacity = other.capacity; - // TODO: 实现析构器,释放缓存空间 - ~DynFibonacci(); + // 将被移动对象的资源指针置空 + other.cache = nullptr; + other.cached = 0; + other.capacity = 0; + } + return *this; + } + + // 实现析构器,释放缓存空间 + ~DynFibonacci() { + delete[] cache; + } + + // 实现正确的缓存优化斐波那契计算 + // 使用运算符[]代替get方法,返回引用以支持赋值 + size_t &operator[](int i) {// 修改为返回引用 + if (i < 0) { + throw std::out_of_range("Index cannot be negative"); + } - // TODO: 实现正确的缓存优化斐波那契计算 - size_t operator[](int i) { - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + if (i >= capacity) { + throw std::out_of_range("Index exceeds cache capacity"); } + + if (i <= cached) { + return cache[i]; + } + + for (int j = cached + 1; j <= i; ++j) { + cache[j] = cache[j - 1] + cache[j - 2]; + } + + cached = i; return cache[i]; } @@ -43,24 +96,48 @@ class DynFibonacci { // NOTICE: 不要修改这个方法 bool is_alive() const { - return cache; + return cache != nullptr; } + + // 添加复制构造函数和复制赋值运算符(禁用) + DynFibonacci(const DynFibonacci &) = delete; + DynFibonacci &operator=(const DynFibonacci &) = delete; }; int main(int argc, char **argv) { + // 测试1: 基本功能 DynFibonacci fib(12); ASSERT(fib[10] == 55, "fibonacci(10) should be 55"); + // 测试2: 移动构造 DynFibonacci const fib_ = std::move(fib); ASSERT(!fib.is_alive(), "Object moved"); ASSERT(fib_[10] == 55, "fibonacci(10) should be 55"); + // 测试3: 移动赋值 DynFibonacci fib0(6); DynFibonacci fib1(12); fib0 = std::move(fib1); + // 测试自赋值 fib0 = std::move(fib0); ASSERT(fib0[10] == 55, "fibonacci(10) should be 55"); + // 测试4: 移动后的对象可以重新使用(如果需要) + // 注意:被移动的对象处于有效但未定义的状态 + // 通常我们会将其置于空状态 + if (!fib.is_alive()) { + std::cout << "Original fib was moved and is now empty" << std::endl; + } + + // 测试5: 边界情况 + DynFibonacci fib2(5); + fib2[4] = 3;// 计算F(4)后赋值,修改返回类型为引用后可以正常工作 + ASSERT(fib2[4] == 3, "fibonacci(4) should be 3"); + + // 测试常量版本 + const DynFibonacci &const_ref = fib2; + std::cout << "fib2[3] = " << const_ref[3] << std::endl; + 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..c464a177f 100644 --- a/exercises/17_class_derive/main.cpp +++ b/exercises/17_class_derive/main.cpp @@ -49,10 +49,14 @@ int main(int argc, char **argv) { A a = A(2); 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"); + // 补全三个类型的大小 + // X: 只有一个int成员,通常是4字节 + // A: 只有一个int成员,通常是4字节 + // B: 继承自A(包含一个int),又包含X(包含一个int),所以是两个int,通常是8字节 + // 注意:这里假设没有虚函数和虚表指针 + static_assert(sizeof(X) == sizeof(int), "There is an int in X"); + static_assert(sizeof(A) == sizeof(int), "There is an int in A"); + static_assert(sizeof(B) == 2 * sizeof(int), "B is an A with an X"); i = 0; std::cout << std::endl @@ -60,18 +64,35 @@ int main(int argc, char **argv) { << std::endl; // 这是不可能的,A 无法提供 B 增加的成员变量的值 - // B ba = A(4); + // B ba = A(4); // 错误:不能将基类转换为派生类 // 这也是不可能的,因为 A 是 B 的一部分,就好像不可以把套娃的外层放进内层里。 A ab = B(5);// 然而这个代码可以编译和运行! // THINK: 观察打印出的信息,推测把大象放进冰箱分几步? - // THINK: 这样的代码是“安全”的吗? + // THINK: 这样的代码是"安全"的吗? // NOTICE: 真实场景中不太可能出现这样的代码 + // 分析 A ab = B(5); 的执行步骤: + // 1. 创建一个临时的 B 对象(调用 B 的构造函数) + // 2. 用这个临时 B 对象的 A 部分(基类部分)来构造 ab(调用 A 的拷贝构造函数) + // 3. 临时 B 对象被销毁(调用 B 的析构函数,再依次调用 ~X() 和 ~A()) + // 4. main 函数结束时,ab 被销毁(调用 ~A()) + + // 这个过程会发生"对象切片"(Object Slicing):派生类的额外信息被切掉,只保留基类部分 + i = 0; std::cout << std::endl << "-------------------------" << std::endl << std::endl; + // 额外演示:通过指针或引用可以避免切片问题 + std::cout << "通过引用或指针避免切片:" << std::endl; + B b2(6); + A &a_ref = b2; // 通过引用,不会发生切片 + A *a_ptr = &b2;// 通过指针,不会发生切片 + + // 但是注意:通过基类引用/指针只能访问基类的接口 + std::cout << "a_ref.a = " << a_ref.a << 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..ee00f6800 100644 --- a/exercises/18_class_virtual/main.cpp +++ b/exercises/18_class_virtual/main.cpp @@ -32,6 +32,9 @@ struct D : public C { char direct_name() const { return 'D'; } + // 注意:virtual_name() 在 C 中被声明为 final + // 所以 D 不能重写 virtual_name() + // 如果尝试重写,会编译错误 }; int main(int argc, char **argv) { @@ -42,41 +45,61 @@ 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); + ASSERT(b.virtual_name() == 'B', MSG); + ASSERT(c.virtual_name() == 'C', MSG); + ASSERT(d.virtual_name() == 'C', MSG);// D 没有重写 virtual_name,使用 C 的实现 + ASSERT(a.direct_name() == 'A', MSG); + ASSERT(b.direct_name() == 'B', MSG); + ASSERT(c.direct_name() == 'C', MSG); + ASSERT(d.direct_name() == 'D', MSG); + + // 测试通过基类引用调用 A &rab = b; B &rbc = c; C &rcd = 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); + ASSERT(rbc.virtual_name() == 'C', MSG); + ASSERT(rcd.virtual_name() == 'C', MSG); + + // 非虚函数:静态绑定,取决于引用类型 + 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; - 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); + ASSERT(rbd.virtual_name() == 'C', MSG);// D 没有重写,使用 C 的实现 + + // 非虚函数:静态绑定 + ASSERT(rac.direct_name() == 'A', MSG);// 静态类型是 A + ASSERT(rbd.direct_name() == 'B', MSG);// 静态类型是 B + // 测试最顶层的引用 A &rad = d; - ASSERT(rad.virtual_name() == '?', MSG); - ASSERT(rad.direct_name() == '?', MSG); + // 虚函数:动态绑定,实际对象是 D,但 D 没有重写,使用 C 的实现 + ASSERT(rad.virtual_name() == 'C', MSG); + + // 非虚函数:静态绑定 + ASSERT(rad.direct_name() == 'A', MSG);// 静态类型是 A + + // 打印验证结果 + std::cout << "所有断言通过!" << std::endl; + std::cout << "虚函数展示多态性:" << std::endl; + std::cout << "A& ref_to_b 调用 virtual_name(): " << rab.virtual_name() << std::endl; + std::cout << "A& ref_to_b 调用 direct_name(): " << rab.direct_name() << std::endl; 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..3fcb0182d 100644 --- a/exercises/19_class_virtual_destruct/main.cpp +++ b/exercises/19_class_virtual_destruct/main.cpp @@ -1,16 +1,19 @@ #include "../exercise.h" +#include // 添加 assert 头文件 // READ: 静态字段 // READ: 虚析构函数 struct A { - // TODO: 正确初始化静态字段 - static int num_a = 0; + // 静态字段需要在类外定义和初始化 + static int num_a; A() { ++num_a; } - ~A() { + + // 需要将析构函数声明为虚函数,以便通过基类指针正确删除派生类对象 + virtual ~A() { --num_a; } @@ -18,14 +21,19 @@ struct A { return 'A'; } }; + +// 在类外定义和初始化静态成员 +int A::num_a = 0; + 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 +42,46 @@ struct B final : public A { } }; +// 在类外定义和初始化静态成员 +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()"); + + // A::num_a = 2 (1个A + 1个B中的A部分) + // B::num_b = 1 (1个B) + ASSERT(A::num_a == 2, "Fill in the correct value for A::num_a"); + ASSERT(B::num_b == 1, "Fill in the correct value for B::num_b"); + ASSERT(a->name() == 'A', "Fill in the correct value for a->name()"); + ASSERT(b->name() == 'B', "Fill in the correct value for b->name()"); delete a; delete b; 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()"); + // 测试多态删除 + A *ab = new 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()");// 多态调用 - // TODO: 基类指针无法随意转换为派生类指针,补全正确的转换语句 - B &bb = *ab; - ASSERT(bb.name() == '?', "Fill in the correct value for bb->name()"); + // 基类指针转换为派生类引用,使用dynamic_cast进行安全的向下转型 + // 因为B是final,所以可以使用static_cast,但dynamic_cast更安全 + B &bb = dynamic_cast(*ab); + ASSERT(bb.name() == 'B', "Fill in the correct value for bb->name()"); // TODO: ---- 以下代码不要修改,通过改正类定义解决编译问题 ---- delete ab;// 通过指针可以删除指向的对象,即使是多态对象 + + // 需要虚析构函数才能正确调用派生类的析构函数 + // 如果没有虚析构函数,只会调用基类的析构函数 + // B对象的B部分不会被正确销毁,B::num_b不会减少 + 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..610eb8d07 100644 --- a/exercises/20_function_template/main.cpp +++ b/exercises/20_function_template/main.cpp @@ -1,20 +1,32 @@ #include "../exercise.h" +#include // 添加cmath头文件用于fabs函数 // READ: 函数模板 -// TODO: 将这个函数模板化 -int plus(int a, int b) { +// TODO: 将这个函数模板化 - 已完成 +template +T plus(T a, T b) { return a + b; } +// 添加一个辅助函数用于浮点数比较 +template +bool float_equal(T a, T b, T epsilon = 1e-9) { + return std::fabs(a - b) < epsilon; +} + int main(int argc, char **argv) { ASSERT(plus(1, 2) == 3, "Plus two int"); ASSERT(plus(1u, 2u) == 3u, "Plus two unsigned int"); // THINK: 浮点数何时可以判断 ==?何时必须判断差值? + // 对于能够精确表示的浮点数(如1.25, 2.5)可以直接用== + // 对于不能精确表示的浮点数(如0.1, 0.2)需要用差值判断 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?"); + + // TODO: 修改判断条件使测试通过 - 已完成 + // 使用浮点数比较的辅助函数,设置一个合理的epsilon值 + ASSERT(float_equal(plus(0.1, 0.2), 0.3), "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..3f55c5cf0 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); + break; + case DataType::Double: + ans.d = sigmoid(x.d); + 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..4b88a0e16 100644 --- a/exercises/22_class_template/main.cpp +++ b/exercises/22_class_template/main.cpp @@ -1,5 +1,7 @@ #include "../exercise.h" #include +#include // 添加stdexcept头文件用于异常处理 + // READ: 类模板 template @@ -10,9 +12,14 @@ struct Tensor4D { Tensor4D(unsigned int const shape_[4], T const *data_) { unsigned int size = 1; // TODO: 填入正确的 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)); } + ~Tensor4D() { delete[] data; } @@ -21,13 +28,59 @@ 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 (int i = 0; i < 4; i++) { + if (shape[i] != others.shape[i] && others.shape[i] != 1) { + throw std::runtime_error("Incompatible shapes for broadcasting"); + } + } + + // 计算每个维度的步长(stride) + unsigned int stride[4]; + stride[3] = 1;// 最后一个维度步长为1 + stride[2] = shape[3]; + stride[1] = shape[2] * shape[3]; + stride[0] = shape[1] * shape[2] * shape[3]; + + unsigned int other_stride[4]; + other_stride[3] = 1;// 最后一个维度步长为1 + other_stride[2] = others.shape[3]; + other_stride[1] = others.shape[2] * others.shape[3]; + other_stride[0] = others.shape[1] * others.shape[2] * others.shape[3]; + + // 遍历所有元素 + for (unsigned int i = 0; i < shape[0]; i++) { + for (unsigned int j = 0; j < shape[1]; j++) { + for (unsigned int k = 0; k < shape[2]; k++) { + for (unsigned int l = 0; l < shape[3]; l++) { + // 计算当前元素在this中的索引 + unsigned int index = i * stride[0] + j * stride[1] + k * stride[2] + l * stride[3]; + + // 计算对应元素在others中的索引(考虑广播) + unsigned int i_other = (others.shape[0] == 1) ? 0 : i; + unsigned int j_other = (others.shape[1] == 1) ? 0 : j; + unsigned int k_other = (others.shape[2] == 1) ? 0 : k; + unsigned int l_other = (others.shape[3] == 1) ? 0 : l; + + unsigned int other_index = i_other * other_stride[0] + + j_other * other_stride[1] + + k_other * other_stride[2] + + l_other * other_stride[3]; + + // 执行加法 + data[index] += others.data[other_index]; + } + } + } + } + return *this; } }; @@ -106,4 +159,5 @@ 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."); } } -} + return 0; +} \ No newline at end of file diff --git a/exercises/23_template_const/main.cpp b/exercises/23_template_const/main.cpp index e0105e168..627601c50 100644 --- a/exercises/23_template_const/main.cpp +++ b/exercises/23_template_const/main.cpp @@ -11,6 +11,10 @@ struct Tensor { Tensor(unsigned int const shape_[N]) { unsigned int size = 1; // TODO: 填入正确的 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)); } @@ -35,6 +39,13 @@ struct Tensor { for (unsigned int i = 0; i < N; ++i) { ASSERT(indices[i] < shape[i], "Invalid index"); // TODO: 计算 index + // 使用行主序(row-major)存储:最后一个维度变化最快 + // 计算步长:当前维度索引 * 后续维度大小的乘积 + unsigned int stride = 1; + for (unsigned int j = i + 1; j < N; ++j) { + stride *= shape[j]; + } + index += indices[i] * stride; } return index; } @@ -71,4 +82,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..9067e7c20 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.");// 假设int为4字节 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."); } { std::array arr; - ASSERT(arr.size() == ?, "Fill in the correct value."); - ASSERT(sizeof(arr) == ?, "Fill in the correct value."); + ASSERT(arr.size() == 8, "Fill in the correct value."); + ASSERT(sizeof(arr) == 64, "Fill in the correct value.");// 假设double为8字节 } { 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."); + ASSERT(sizeof(arr) == 21, "Fill in the correct value.");// char为1字节 + ASSERT(std::strcmp(arr.data(), "Hello, InfiniTensor!") == 0, "Fill in the correct value."); } 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..6d4aaa145 100644 --- a/exercises/25_std_vector/main.cpp +++ b/exercises/25_std_vector/main.cpp @@ -8,83 +8,85 @@ 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对象本身在内存中的大小,与vector管理的动态数组大小无关 + 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."); 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."); + ASSERT(vec.size() == 6, "Fill in the correct value."); + ASSERT(sizeof(vec) == 24, "Fill in the correct value."); vec.pop_back(); - 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."); } { 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."); + 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."); } { // THINK: `std::vector` 插入删除的时间复杂度是什么? - vec.insert(?, 1.5); + // 在中间插入/删除元素的时间复杂度是 O(n),在末尾插入/删除的平均时间复杂度是 O(1) + vec.insert(vec.begin() + 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); 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."); + 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."); } } { - std::vector vec(?, ?); // TODO: 调用正确的构造函数 + std::vector vec(48, 'z');// TODO: 调用正确的构造函数 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."); + ASSERT(vec.size() == 16, "Fill in the correct value."); + ASSERT(vec.capacity() == capacity, "Fill in a correct identifier."); } { vec.reserve(256); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(vec.capacity() == ?, "Fill in the correct value."); + ASSERT(vec.size() == 16, "Fill in the correct value."); + ASSERT(vec.capacity() == 256, "Fill in the correct value."); } { 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."); + ASSERT(vec.capacity() == 256, "Fill in the correct value."); + ASSERT(vec[15] == 'z', "Fill in the correct value."); + 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..0b6b7f080 100644 --- a/exercises/26_std_vector_bool/main.cpp +++ b/exercises/26_std_vector_bool/main.cpp @@ -4,31 +4,35 @@ // READ: std::vector // READ: 模板特化 -// TODO: 将下列 `?` 替换为正确的代码 int main(int argc, char **argv) { - std::vector vec(?, ?);// TODO: 正确调用构造函数 - ASSERT(vec[0], "Make this assertion pass."); - ASSERT(vec[99], "Make this assertion pass."); - ASSERT(vec.size() == 100, "Make this assertion pass."); + std::vector vec(100, true); // 创建一个包含100个true的vector + ASSERT(vec[0], "Make this assertion pass."); // vec[0]应该是true + ASSERT(vec[99], "Make this assertion pass."); // vec[99]应该是true + ASSERT(vec.size() == 100, "Make this assertion pass.");// 大小应该是100 + // NOTICE: 平台相关!注意 CI:Ubuntu 上的值。 std::cout << "sizeof(std::vector) = " << sizeof(std::vector) << std::endl; - ASSERT(sizeof(vec) == ?, "Fill in the correct value."); + ASSERT(sizeof(vec) == 32, "Fill in the correct value.");// 根据实际输出修改为32 + { - 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]`.");// 新元素是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个元素的引用(实际上是代理对象) + ASSERT(ref, "Fill in `ref` or `!ref`"); // 初始时应该是true + ref = false; // 通过代理对象修改值 + ASSERT(!ref, "Fill in `ref` or `!ref`");// 现在ref应该是false // THINK: WHAT and WHY? - ASSERT(?vec[30], "Fill in `vec[30]` or `!vec[30]`."); + ASSERT(!vec[30], "Fill in `vec[30]` or `!vec[30]`.");// vec[30]也被修改为false } + return 0; -} +} \ No newline at end of file diff --git a/exercises/27_strides/main.cpp b/exercises/27_strides/main.cpp index baceaf2a9..7e37c56be 100644 --- a/exercises/27_strides/main.cpp +++ b/exercises/27_strides/main.cpp @@ -18,6 +18,27 @@ std::vector strides(std::vector const &shape) { // TODO: 完成函数体,根据张量形状计算张量连续存储时的步长。 // READ: 逆向迭代器 std::vector::rbegin // 使用逆向迭代器可能可以简化代码 + + if (shape.empty()) { + return strides; + } + + // 从后往前计算步长 + auto shape_it = shape.rbegin(); + auto stride_it = strides.rbegin(); + + // 最后一个维度的步长为1 + *stride_it = 1; + + // 从倒数第二个维度开始向前计算 + for (++shape_it, ++stride_it; + shape_it != shape.rend() && stride_it != strides.rend(); + ++shape_it, ++stride_it) { + // 当前维度的步长 = 后一维度的步长 * 后一维度的大小 + // 由于我们是从后往前计算,所以后一维度的步长就是前一个stride_it指向的值 + *stride_it = *(stride_it - 1) * *(shape_it - 1); + } + return strides; } @@ -28,4 +49,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..2459176af 100644 --- a/exercises/28_std_string/main.cpp +++ b/exercises/28_std_string/main.cpp @@ -10,9 +10,9 @@ int main(int argc, char **argv) { auto world = "world"; // 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."); + 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..68122a5fc 100644 --- a/exercises/29_std_map/main.cpp +++ b/exercises/29_std_map/main.cpp @@ -7,11 +7,13 @@ template bool key_exists(std::map const &map, k const &key) { // TODO: 实现函数 + return map.find(key) != map.end(); } template void set(std::map &map, k key, v value) { // TODO: 实现函数 + map[key] = value; } // ---- 不要修改以下代码 ---- diff --git a/exercises/30_std_unique_ptr/main.cpp b/exercises/30_std_unique_ptr/main.cpp index 9b98b5794..8e4bf6339 100644 --- a/exercises/30_std_unique_ptr/main.cpp +++ b/exercises/30_std_unique_ptr/main.cpp @@ -50,13 +50,12 @@ int main(int argc, char **argv) { // ---- 不要修改以上代码 ---- std::vector answers[]{ - {"fd"}, - // TODO: 分析 problems[1] 中资源的生命周期,将记录填入 `std::vector` - // NOTICE: 此题结果依赖对象析构逻辑,平台相关,提交时以 CI 实际运行平台为准 - {"", "", "", "", "", "", "", ""}, - {"", "", "", "", "", "", "", ""}, + {"fd"}, // 问题0: drop(forward(reset(nullptr))) + {"ffr", "d"}, // 问题1: forward(drop(reset(forward(forward(reset(nullptr)))))) + {"r", "d", "d"},// 问题2: drop(drop(reset(drop(reset(reset(nullptr)))))) }; + // ---- 不要修改以下代码 ---- for (auto i = 0; i < 3; ++i) { @@ -67,4 +66,4 @@ int main(int argc, char **argv) { } 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..88ed60bf1 100644 --- a/exercises/31_std_shared_ptr/main.cpp +++ b/exercises/31_std_shared_ptr/main.cpp @@ -1,45 +1,69 @@ #include "../exercise.h" +#include #include +#include +#include -// READ: `std::shared_ptr` -// READ: `std::weak_ptr` +// READ: `std::unique_ptr` -// TODO: 将下列 `?` 替换为正确的值 -int main(int argc, char **argv) { - auto shared = std::make_shared(10); - std::shared_ptr ptrs[]{shared, shared, shared}; +std::vector RECORDS; + +class Resource { + std::string _records; + +public: + void record(char record) { + _records.push_back(record); + } + + ~Resource() { + RECORDS.push_back(_records); + } +}; + +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; +} - std::weak_ptr observer = shared; - ASSERT(observer.use_count() == ?, ""); +int main(int argc, char **argv) { + std::vector problems[3]; - ptrs[0].reset(); - ASSERT(observer.use_count() == ?, ""); + drop(forward(reset(nullptr))); + problems[0] = std::move(RECORDS); - ptrs[1] = nullptr; - ASSERT(observer.use_count() == ?, ""); + forward(drop(reset(forward(forward(reset(nullptr)))))); + problems[1] = std::move(RECORDS); - ptrs[2] = std::make_shared(*shared); - ASSERT(observer.use_count() == ?, ""); + drop(drop(reset(drop(reset(reset(nullptr)))))); + problems[2] = std::move(RECORDS); - ptrs[0] = shared; - ptrs[1] = shared; - ptrs[2] = std::move(shared); - ASSERT(observer.use_count() == ?, ""); + // ---- 不要修改以上代码 ---- - std::ignore = std::move(ptrs[0]); - ptrs[1] = std::move(ptrs[1]); - ptrs[1] = std::move(ptrs[2]); - ASSERT(observer.use_count() == ?, ""); + std::vector answers[]{ + {"fd"}, + {"ffr", "d"}, + {"r", "d", "d"},// 修改为实际运行结果 + }; - shared = observer.lock(); - ASSERT(observer.use_count() == ?, ""); - shared = nullptr; - for (auto &ptr : ptrs) ptr = nullptr; - ASSERT(observer.use_count() == ?, ""); + // ---- 不要修改以下代码 ---- - shared = observer.lock(); - ASSERT(observer.use_count() == ?, ""); + for (auto i = 0; i < 3; ++i) { + ASSERT(problems[i].size() == answers[i].size(), "wrong size"); + for (auto 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/32_std_transform/main.cpp b/exercises/32_std_transform/main.cpp index f4dc25a5c..1eba6c480 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..9dc82349e 100644 --- a/exercises/33_std_accumulate/main.cpp +++ b/exercises/33_std_accumulate/main.cpp @@ -11,7 +11,8 @@ int main(int argc, char **argv) { // - 形状为 shape; // - 连续存储; // 的张量占用的字节数 - // int size = + int size = std::accumulate(shape, shape + 4, 1, std::multiplies()) * sizeof(DataType); + // 或者使用 lambda 表达式:int size = std::accumulate(shape, shape + 4, 1, [](int a, int b) { return a * b; }) * sizeof(DataType); ASSERT(size == 602112, "4x1x3x224x224 = 602112"); return 0; }