实验1 现代C++编程初体验
实验任务1
源代码task1.cpp
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <algorithm> 5 6 // 模板函数声明 7 template<typename T> 8 void output(const T& c); 9 10 void test1(); 11 void test2(); 12 void test3(); 13 14 int main() { 15 std::cout << "测试1: \n"; 16 test1(); 17 std::cout << "\n测试2: \n"; 18 test2(); 19 std::cout << "\n测试3: \n"; 20 test3(); 21 } 22 23 // 输出容器对象c中的元素 24 template <typename T> 25 void output(const T& c) { 26 for (auto& i : c) 27 std::cout << i << ' '; 28 std::cout << '\n'; 29 } 30 // 测试1:组合使用算法库、迭代器、string反转字符串 31 void test1() { 32 using namespace std; 33 string s0{ "0123456789" }; 34 cout << "s0 = " << s0 << endl; 35 string s1(s0); 36 // 反转s1自身 37 reverse(s1.begin(), s1.end()); 38 cout << "s1 = " << s1 << endl; 39 string s2(s0.size(), ' '); 40 // 将s0反转后结果拷贝到s2, s0自身不变 41 reverse_copy(s0.begin(), s0.end(), s2.begin()); 42 cout << "s2 = " << s2 << endl; 43 } 44 // 测试2:组合使用算法库、迭代器、vector反转动态数组对象vector内数据 45 void test2() { 46 using namespace std; 47 vector<int> v0{ 2, 0, 4, 9 }; 48 cout << "v0: "; output(v0); 49 vector<int> v1{ v0 }; 50 reverse(v1.begin(), v1.end()); 51 cout << "v1: "; output(v1); 52 vector<int> v2{ v0 }; 53 reverse_copy(v0.begin(), v0.end(), v2.begin()); 54 cout << "v2: "; output(v2); 55 } 56 // 测试3:组合使用算法库、迭代器、vector实现元素旋转移位 57 void test3() { 58 using namespace std; 59 vector<int> v0{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 60 cout << "v0: "; output(v0); 61 vector<int> v1{ v0 }; 62 // 将[v1.begin(), v1.end())区间内元素循环左移1位 63 rotate(v1.begin(), v1.begin() + 1, v1.end()); 64 cout << "v1: "; output(v1); 65 vector<int> v2{ v0 }; 66 // 将[v1.begin(), v1.end())区间内元素循环左移2位 67 rotate(v2.begin(), v2.begin() + 2, v2.end()); 68 cout << "v2: "; output(v2); 69 vector<int> v3{ v0 }; 70 // 将[v1.begin(), v1.end())区间内元素循环右移1位 71 rotate(v3.begin(), v3.end() - 1, v3.end()); 72 cout << "v3: "; output(v3); 73 vector<int> v4{ v0 }; 74 // 将[v1.begin(), v1.end())区间内元素循环右移2位 75 rotate(v4.begin(), v4.end() - 2, v4.end()); 76 cout << "v4: "; output(v4); 77 }
运行结果截图:

问题1: reverse 和 reverse_copy 有什么区别?
答:reverse是直接在原来的容器上修改,将容器自身反转。
reverse_copy是将源容器反转后的结果拷贝到另一个容器或位置,源容器不变。
问题2:rotate 算法是如何改变元素顺序的?它的三个参数分别代表什么?
答:rotate 算法通过将范围内的元素分成两部分并交换它们的位置来改变顺序。它的调用形式是 std::rotate(first, middle, last)。其中,first 和 last 定义了旋转操作的整个范围 [first, last);而 middle 是范围内的迭代器,它指定了旋转后的新的起始点,即原先在 middle 位置的元素将成为旋转后序列的第一个元素。算法会将 [first, middle) 这段元素移到序列的末尾,并将 [middle, last) 这段元素移到序列的开头。
实验任务2
源代码task2.cpp
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <numeric> 5 #include <iomanip> 6 #include <cstdlib> 7 #include <ctime> 8 // 模板函数声明 9 template<typename T> 10 void output(const T& c); 11 int generate_random_number(); 12 void test1(); 13 void test2(); 14 int main() { 15 std::srand(std::time(0)); // 添加随机种子 16 std::cout << "测试1: \n"; 17 test1(); 18 std::cout << "\n测试2: \n"; 19 test2(); 20 } 21 // 输出容器对象c中的元素 22 template <typename T> 23 void output(const T& c) { 24 for (auto& i : c) 25 std::cout << i << ' '; 26 std::cout << '\n'; 27 } 28 // 返回[0, 100]区间内的一个随机整数 29 int generate_random_number() { 30 return std::rand() % 101; 31 } 32 // 测试1:对容器类对象指定迭代器区间赋值、排序 33 void test1() { 34 using namespace std; 35 vector<int> v0(10); // 创建一个动态数组对象v0, 对象大小为10 36 generate(v0.begin(), v0.end(), generate_random_number); // 生成随机数填充v0,第三个参数为生成器函数 37 cout << "v0: "; output(v0); 38 vector<int> v1{ v0 }; 39 sort(v1.begin(), v1.end()); // 对整个vector排序 40 cout << "v1: "; output(v1); 41 vector<int> v2{ v0 }; 42 sort(v2.begin() + 1, v2.end() - 1); // 只对中间部分排序,不包含首尾元素 43 cout << "v2: "; output(v2); 44 } 45 // 测试2:对容器类对象指定迭代器区间赋值、计算最大值/最小值/均值 46 void test2() { 47 using namespace std; 48 vector<int> v0(10); 49 generate(v0.begin(), v0.end(), generate_random_number); 50 cout << "v0: "; output(v0); 51 // 求最大值和最小值 52 auto min_iter = min_element(v0.begin(), v0.end()); //返回对应最小元素的迭代器 53 auto max_iter = max_element(v0.begin(), v0.end()); 54 cout << "最小值: " << *min_iter << endl; 55 cout << "最大值: " << *max_iter << endl; 56 // 同时求最大值和最小值 57 auto ans = minmax_element(v0.begin(), v0.end()); 58 cout << "最小值: " << *(ans.first) << endl; 59 cout << "最大值: " << *(ans.second) << endl; 60 // 求平均值 61 double avg1 = accumulate(v0.begin(), v0.end(), 0.0) / v0.size(); 62 //来自#include <iomanip>,默认格式下 setprecision(n) 控制的是总有效位数,而 fixed 模式下它控制的是小数点后的位数。 63 cout << "均值: " << fixed << setprecision(2) << avg1 << endl; 64 sort(v0.begin(), v0.end()); 65 double avg2 = accumulate(v0.begin() + 1, v0.end() - 1, 0.0) / (v0.size() - 2); 66 cout << "去掉最大值、最小值之后,均值: " << avg2 << endl; 67 }
运行结果截图:

问题1: generate 算法的作用是什么?
答:generate 算法的作用是使用一个生成器函数为容器指定范围内的所有元素赋值。
问题2: minmax_element 和分别调用 min_element 、 max_element 相比,有什么优势?
答:minmax_element只需要一次遍历就可以同时获得最大值和最小值,而分别调用 min_element 、 max_element则需要两次遍历,因此minmax_element具有更高的效率和性能
问题3:查询 generate 第3个参数 [](){return std::rand()%101;} 用法,与使用自定义函数generate_random_number 相比,lambda表达式适用场景是什么?
答:Lambda 表达式特别适用于那种只在当前位置使用一次、逻辑非常简单,并且可能需要捕获周围局部变量的函数对象。
实验任务3
源代码task3.cpp
源代码task3.cpp
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <cctype> 5 unsigned char func(unsigned char c); 6 void test1(); 7 void test2(); 8 int main() { 9 std::cout << "测试1: 字符串大小写转换\n"; 10 test1(); 11 std::cout << "\n测试2: 字符变换\n"; 12 test2(); 13 } 14 unsigned char func(unsigned char c) { 15 if (c == 'z') 16 return 'a'; 17 if (c == 'Z') 18 return 'A'; 19 if (std::isalpha(c)) 20 return static_cast<unsigned char>(c + 1); 21 return c; 22 } 23 void test1() { 24 std::string s1{ "Hello World 2049!" }; 25 std::cout << "s1 = " << s1 << '\n'; 26 std::string s2; 27 for (auto c : s1) 28 s2 += std::tolower(c); 29 std::cout << "s2 = " << s2 << '\n'; 30 std::string s3; 31 for (auto c : s1) 32 s3 += std::toupper(c); 33 std::cout << "s3 = " << s3 << '\n'; 34 } 35 void test2() { 36 std::string s1{ "I love cosmos!" }; 37 std::cout << "s1 = " << s1 << '\n'; 38 std::string s2(s1.size(), ' '); 39 std::transform(s1.begin(), s1.end(),s2.begin(),func); 40 std::cout << "s2 = " << s2 << '\n'; 41 }
运行结果截图:

问题1:自定义函数 func 功能是什么?
答 : func 的功能是针对输入的字符,如果为字母则输出其字母表对应的后一个字母(z变为a,Z变为A),若不是字母则输出源字符,从而起到加密的作用。
问题2: tolower 和 toupper 功能分别是什么?
答:tolower 和 toupper 功能分别将输入的字符转换为其对应的小写和大写字母,如果输入的不是字母或已经是小写或大写字符则输出原字符。
问题3: transform 的4个参数意义分别是什么?如果把第3个参数 s2.begin() 改成 s1.begin() ,有何区别?
答:transform 算法有四个参数:前两个参数定义了源范围 [first, last),即要读取和转换的元素区间;第三个参数 result 定义了目标范围的起始迭代器,转换后的结果将从这里开始写入;第四个参数 op 是一个一元操作函数,它定义了对源范围中每个元素应用的转换规则。
如果把第 3 个参数从 s2.begin() 改成 s1.begin(),操作将从非原地 转换变为原地转换:原先的结果是写入到新容器 s2,而 s1 保持不变;修改后,结果将直接覆盖源容器 s1 的内容。
实验任务4
源代码task4.cpp
源代码task4.cpp
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 bool is_palindrome(const std::string& s); 5 bool is_palindrome_ignore_case(const std::string& s); 6 int main() { 7 using namespace std; 8 string s; 9 // 多组输入,直到按下Ctrl+Z结束测试 10 while (cin >> s) { 11 cout << boolalpha 12 << "区分大小写: " << is_palindrome(s) << "\n" 13 << "不区分大小写: " << is_palindrome_ignore_case(s) << "\n\n"; 14 } 15 } 16 // 函数is_palindrome定义 17 bool is_palindrome(const std::string& s) { 18 std::string s1{ s }; 19 std::reverse(s1.begin(), s1.end()); 20 return s == s1; 21 } 22 23 // 函数is_palindrome_ignore_case定义 24 bool is_palindrome_ignore_case(const std::string& s) { 25 std::string s2{ s }; 26 std::transform(s2.begin(), s2.end(), s2.begin(), [](unsigned char c) {return std::tolower(c); }); 27 return is_palindrome(s2); 28 }
运行结果截图:

问题:使用 cin >> s 输入时,输入的字符串中不能包含空格。如果希望测试字符串包含空格(如 hello oop ),代码应如何调整?
答:使用标准库getline可以读取整行,包括空格
1 while (getline(cin, s)) { 2 cout << boolalpha 3 << "区分大小写: " << is_palindrome(s) << "\n" 4 << "不区分大小写: " << is_palindrome_ignore_case(s) << "\n\n"; 5 }

实验任务5
源代码task5.cpp
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 5 std::string dec2n(int x, int n = 2); 6 int main() { 7 int x; 8 while (std::cin >> x) { 9 std::cout << "十进制: " << x << '\n' 10 << "二进制: " << dec2n(x) << '\n' 11 << "八进制: " << dec2n(x, 8) << '\n' 12 << "十二进制: " << dec2n(x, 12) << '\n' 13 << "十六进制: " << dec2n(x, 16) << '\n' 14 << "三十二进制: " << dec2n(x, 32) << "\n\n"; 15 } 16 } 17 // 函数dec2n定义 18 std::string dec2n(int x, int n) { 19 std::string result; 20 char map[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 21 do { 22 result += map[x % n]; 23 x /= n; 24 } while (x != 0); 25 std::reverse(result.begin(), result.end()); 26 return result; 27 }
运行结果截图:

实验任务6
源代码task6.cpp
1 #include<iostream> 2 #include<iomanip> 3 int main() 4 { 5 using namespace std; 6 cout << setw(2) << " "; 7 for (int i = 0; i < 26; i++) 8 cout << right << setw(2) << static_cast<char>('a' + i); 9 cout << endl; 10 char map[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 11 for (int i = 1; i <= 26; i++) 12 { 13 cout << right << setw(2) << i; 14 for (int j = 0; j < 26; j++) 15 cout << right << setw(2) << map[(i + j) % 26]; 16 cout << endl; 17 } 18 }
运行结果截图:

实验任务7
源代码task7.cpp
1 #include<iostream> 2 #include<iomanip> 3 4 int generate_num(); 5 char generte_op(); 6 int generate_expression(); 7 8 int main() 9 { 10 using namespace std; 11 srand(time(0)); 12 int count = 0; 13 for (int i = 0; i < 10; i++) 14 { 15 int ans = generate_expression(); 16 int my_ans; 17 cin >> my_ans; 18 if (my_ans == ans) 19 count++; 20 } 21 cout << "正确率:" << fixed << setprecision(2) << count / 10.0 * 100 << "%"; 22 } 23 24 int generate_num() {//随机生成1-10区间内的正整数 25 return 1 + std::rand() % 10; 26 } 27 28 char generte_op() {//随机生成运算符 29 char op[] = "+-*/"; 30 return op[std::rand() % 4]; 31 } 32 33 int generate_expression() 34 { 35 using namespace std; 36 char op = generte_op(); 37 int a, b; 38 bool flag = true; 39 while (flag) 40 { 41 a = generate_num(); 42 b = generate_num(); 43 if (op == '+' || op == '*') 44 flag = false; 45 else if (op == '-') //保证第一个操作数>=第二个操作数 46 flag = (a < b); 47 else //保证第一个操作数能整除第二个操作数整除 48 flag = (a % b != 0); 49 50 } 51 cout << a << ' ' << op << ' ' << b << ' ' << "= "; 52 switch (op) 53 { 54 case '+':return a + b; 55 case '-':return a - b; 56 case '*':return a * b; 57 case '/':return a / b; 58 default: 59 break; 60 } 61 62 }
运行结果截图:


浙公网安备 33010602011771号