OOP-实验1
实验任务1
源代码task1.cpp
1 // 现代C++标准库、算法库体验 2 // 1. 字符串string、动态数组容器类vector、迭代器 3 // 2. 算法库:反转元素次序、旋转元素 4 // 3. 函数模板、const引用作为形参 5 6 #include <iostream> 7 #include <string> 8 #include <vector> 9 #include <algorithm> // 算法库 10 11 // 函数模板声明 12 template <typename T> 13 void output(const T &c); 14 15 void test1(); 16 void test2(); 17 void test3(); 18 19 int main() 20 { 21 std::cout << "测试1: " << std::endl; 22 test1(); 23 24 std::cout << std::endl; 25 std::cout << "测试2: " << std::endl; 26 test2(); 27 28 std::cout << std::endl; 29 std::cout << "测试3: " << std::endl; 30 test3(); 31 32 return 0; 33 } 34 35 // 输出容器对象c中的元素 36 template <typename T> 37 void output(const T &c) 38 { 39 for (auto &i : c) 40 std::cout << i << " "; 41 std::cout << std::endl; 42 } 43 44 // 测试1: 组合使用算法库、迭代器、string反转字符串 45 void test1() 46 { 47 using namespace std; 48 49 string s0{"0123456789"}; 50 cout << "s0 = " << s0 << endl; 51 52 string s1{s0}; 53 reverse(s1.begin(), s1.end()); 54 cout << "s1 = " << s1 << endl; 55 56 string s2(s0.size(), ' '); // 创建一个空字符串,大小与s0相同 57 // 将s0反转后结果copy到s2中,s0自身不变 58 reverse_copy(s0.begin(), s0.end(), s2.begin()); 59 cout << "s2 = " << s2 << endl; 60 } 61 62 // 测试2: 组合使用算法库、迭代器、vector反转动态数组对象vector内数据 63 void test2() 64 { 65 using namespace std; 66 67 vector<int> v0{2, 0, 4, 9}; 68 cout << "v0: "; 69 output(v0); 70 71 vector<int> v1{v0}; 72 reverse(v1.begin(), v1.end()); 73 cout << "v1: "; 74 output(v1); 75 76 vector<int> v2{v0}; 77 reverse_copy(v0.begin(), v0.end(), v2.begin()); 78 cout << "v2: "; 79 output(v2); 80 } 81 82 // 测试3: 组合使用算法库、迭代器、vector实现元素旋转移位 83 void test3() 84 { 85 using namespace std; 86 87 vector<int> v0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 88 cout << "v0: "; 89 output(v0); 90 91 vector<int> v1{v0}; 92 // 将[v1.begin(),v1.end()]区间内元素循环左移1位 93 rotate(v1.begin(), v1.begin() + 1, v1.end()); 94 cout << "v1: "; 95 output(v1); 96 97 vector<int> v2{v0}; 98 // 将[v2.begin(),v2.end()]区间内元素循环左移2位 99 rotate(v2.begin(), v2.begin() + 2, v2.end()); 100 cout << "v2: "; 101 output(v2); 102 103 vector<int> v3{v0}; 104 // 将[v3.begin(),v3.end()]区间内元素循环右移1位 105 rotate(v3.begin(), v3.end() - 1, v3.end()); 106 cout << "v3: "; 107 output(v3); 108 109 vector<int> v4{v0}; 110 // 将[v4.begin(),v4.end()]区间内元素循环右移2位 111 rotate(v4.begin(), v4.end() - 2, v4.end()); 112 cout << "v4: "; 113 output(v4); 114 }
运行测试截图

回答问题
问题1:reverse 和 reverse_copy 有什么区别?
reverse:直接反转容器内元素,因此要改变原容器内容
reverse_copy:将原容器元素反转后的结果复制到另一个新容器中,且原容器的内容不改变
问题2:rotate 算法是如何改变元素顺序的?它的三个参数分别代表什么?
rotate 即循环移位,第一个参数即循环移位区间的起始迭代器,第三个参数即循环移位区间的终止迭代器;
中间参数:左移k位即 起始迭代器+k ;右移k位即 终止迭代器-k
实验任务2
源代码task2.cpp
1 // 体验使用C++标准库高效完成数据赋值、排序和基本统计 2 3 #include <iostream> 4 #include <vector> 5 #include <algorithm> 6 #include <numeric> 7 #include <iomanip> 8 #include <cstdlib> 9 #include <ctime> 10 11 // 函数模板声明 12 template <typename T> 13 void output(const T &c); 14 15 int generate_random_number(); // 生成随机数 16 void test1(); 17 void test2(); 18 19 int main() 20 { 21 std::srand(std::time(0)); // 设置随机数种子 22 23 std::cout << "测试1: " << std::endl; 24 test1(); 25 26 std::cout << "测试2: " << std::endl; 27 test2(); 28 } 29 30 // 输出容器对象c中的元素 31 template <typename T> 32 void output(const T &c) 33 { 34 for (const auto &i : c) 35 std::cout << i << " "; 36 std::cout << std::endl; 37 } 38 39 // 返回[0, 100]区间内的一个随机整数 40 int generate_random_number() 41 { 42 return std::rand() % 101; 43 } 44 45 // generate(v0.begin(), v0.end(), []() 46 // { return std::rand() % 101; }); 47 48 // 测试1: 对容器类对象指定迭代器区间赋值、排序 49 void test1() 50 { 51 using namespace std; 52 53 vector<int> v0(10); // 创建一个大小为10的动态数组v0 54 generate(v0.begin(), v0.end(), generate_random_number); // 生成随机数填充v0 55 cout << "v0: "; 56 output(v0); 57 58 vector<int> v1{v0}; 59 sort(v1.begin(), v1.end()); 60 cout << "v1: "; 61 output(v1); 62 63 vector<int> v2{v0}; 64 sort(v2.begin() + 1, v2.end() - 1); // 只对除首尾元素外的元素排序 65 cout << "v2: "; 66 output(v2); 67 } 68 69 // 测试2: 对容器类对象指定迭代器区间赋值、计算最大值/最小值/均值 70 void test2() 71 { 72 using namespace std; 73 74 vector<int> v0(10); 75 generate(v0.begin(), v0.end(), generate_random_number); 76 cout << "v0: "; 77 output(v0); 78 79 // 求v0的最大值和最小值 指针 80 auto min_iter = min_element(v0.begin(), v0.end()); 81 cout << "最小值: " << *min_iter << endl; 82 auto max_iter = max_element(v0.begin(), v0.end()); 83 cout << "最大值: " << *max_iter << endl; 84 85 // 同时求v0的最大值和最小值 指针 86 auto ans = minmax_element(v0.begin(), v0.end()); 87 cout << "最小值: " << *(ans.first) << endl; 88 cout << "最大值: " << *(ans.second) << endl; 89 90 // 求v0平均值 91 double avg1 = accumulate(v0.begin(), v0.end(), 0.0) / v0.size(); 92 cout << "平均值: " << fixed << setprecision(2) << avg1 << endl; // 格式控制,保留到小数点后两位 93 94 // 求去掉最大值和最小值后,v0的平均值 95 sort(v0.begin(), v0.end()); 96 double avg2 = accumulate(v0.begin() + 1, v0.end() - 1, 0.0) / (v0.size() - 2); 97 cout << "去掉最大值、最小值后, 平均值: " << avg2 << endl; 98 };
运行测试截图

回答问题
问题1:generate 算法的作用是什么?
generate:对容器类对象指定迭代器区间内赋值
问题2:minmax_element 和分别调用 min_element、max_element 相比,有什么优势?
优势:只需要一次遍历即可同时求出容器类的最大值和最小值
问题3:generate 更改后效果是否相同?类比C++中 lambda 表达式用法。
generate 更改后效果依然相同,为无参的 lambda 表达式;C++中的 lambda 表达式是一种创建匿名函数对象的方式
实验任务3
源代码task3.cpp
1 // 体验使用C++标准库高效完成字符串变换操作 2 3 #include <iostream> 4 #include <string> 5 #include <algorithm> 6 #include <cctype> 7 8 unsigned char func(unsigned char c); // 返回无符号单字符 9 void test1(); 10 void test2(); 11 12 int main() 13 { 14 std::cout << "测试1: 字符串大小写转换" << std::endl; 15 test1(); 16 17 std::cout << std::endl; 18 std::cout << "测试2: 字符变换" << std::endl; 19 test2(); 20 } 21 22 // 字符后移 23 unsigned char func(unsigned char c) 24 { 25 // 单独对字符z和Z作处理 26 if (c == 'z') 27 return 'a'; 28 else if (c == 'Z') 29 return 'A'; 30 else if (std::isalpha(c)) // 判断是否为字母 31 return static_cast<unsigned char>(c + 1); // 静态类型转换 32 33 return c; 34 } 35 36 // 字符串大小写转换 37 void test1() 38 { 39 using namespace std; 40 string s1{"Hello World 2049!"}; 41 cout << "s1 = " << s1 << endl; 42 43 string s2; 44 for (char &c : s1) // 遍历s1一个个作转换 45 { 46 s2 += tolower(c); // 将转换后的字符一个个拼接到s2 47 } 48 cout << "s2 = " << s2 << endl; 49 50 string s3; 51 for (char &c : s1) 52 { 53 s3 += toupper(c); 54 } 55 cout << "s3 = " << s3 << endl; 56 } 57 58 // 字符变换 59 void test2() 60 { 61 using namespace std; 62 63 string s1{"I love cosmos!"}; 64 cout << "s1 = " << s1 << endl; 65 66 string s2(s1.size(), ' '); 67 transform(s1.begin(), s1.end(), s2.begin(), func); 68 cout << "s2 = " << s2 << endl; 69 }
运行测试截图

回答问题
问题1:自定义函数 func 功能是什么?
若该字符为字母,则将其变换成它的下一个字母,其中 z 和 Z 分别变换成 a 和 A
若该字符不是字母,则不作任何处理
问题2:tolower 和 toupper 功能分别是什么?
tolower() 将单个字符转换成它的小写;toupper() 将单个字符转换成它的大写
问题3:transform 的4个参数意义分别是什么?如果把第3个参数 s2.begin() 改成 s1.begin(),有何区别?
transform() 作字符的变换操作,其中前两个参数表示执行操作的范围,第4个参数表示变换操作的具体方法,而第3个参数表示结果的起始存储地址;
若改成 s1.begin() , 则相当于直接对 s1 执行 func 操作,会更改原字符串 s1 的内容,而 s2.begin() 不会更改原字符串 s1 的内容
实验任务4
源代码task4.cpp
1 // 判断回文串 返回true/false 2 // 要求:支持多组输入 3 // 严格区分大小写:bool is_palindrome(const std::string &s) 4 // 不区分大小写:bool is_palindrome_ignore_case(const std::string &s) 5 6 #include <iostream> 7 #include <string> 8 #include <algorithm> 9 10 bool is_palindrome(const std::string &s); 11 bool is_palindrome_ignore_case(const std::string &s); 12 unsigned char func(const char c); 13 14 int main() 15 { 16 using namespace std; 17 string s; 18 19 // 多组输入,直到按下Ctrl+Z结束测试 20 while (cin >> s) 21 { 22 cout << boolalpha 23 << "区分大小写: " << is_palindrome(s) << endl 24 << "不区分大小写: " << is_palindrome_ignore_case(s) << endl 25 << endl; 26 } 27 } 28 29 // 函数is_palindrome()定义 严格区分大小写 30 bool is_palindrome(const std::string &s) 31 { 32 using namespace std; 33 string str(s.size(), ' '); 34 reverse_copy(s.begin(), s.end(), str.begin()); // 得到反转后的字符串str 35 36 // 比较s和str 37 if (s == str) 38 return true; 39 return false; 40 } 41 42 // 函数is_palindrome_ignore_case()定义 不区分大小写 43 bool is_palindrome_ignore_case(const std::string &s) 44 { 45 using namespace std; 46 string str(s.size(), ' '); 47 transform(s.begin(), s.end(), str.begin(), func); 48 49 string str_reverse(s.size(), ' '); 50 reverse_copy(str.begin(), str.end(), str_reverse.begin()); 51 52 if (str == str_reverse) 53 return true; 54 return false; 55 } 56 57 unsigned char func(const char c) 58 { 59 return tolower(c); 60 }
运行测试截图

回答问题
问题:使用 cin >> s 输入时,输入的字符串中不能包含空格。如果希望测试字符串包含空格(如 hello oop ),代码应如何调整?
通过询问 AI,了解到:若希望读取包含空格的整行文本,应该使用 std::getline() 函数,而不是 cin >> s
实验任务5
源代码task5.cpp
1 // 进制转换 2 // std::string dec2n(x, n) 实现把一个十进制数x转换成n进制,结果以字符串形式返回 3 // 若第2个参数n未指定,则默认转换成二进制 4 // 本次实验限定参数n取值区间为[2,36],限定参数x>=0 5 // 要求:支持多组输入 6 7 #include <iostream> 8 #include <string> 9 #include <algorithm> 10 11 std::string dec2n(int x, int n = 2); 12 13 int main() 14 { 15 using namespace std; 16 int x; 17 while (cin >> x) 18 { 19 cout << "十进制: " << x << endl 20 << "二进制: " << dec2n(x) << endl 21 << "八进制: " << dec2n(x, 8) << endl 22 << "十二进制: " << dec2n(x, 12) << endl 23 << "十六进制: " << dec2n(x, 16) << endl 24 << "三十二进制: " << dec2n(x, 32) << endl 25 << endl; 26 } 27 } 28 29 // 函数dec2n()定义 30 // 注意:带有默认形参的函数只需在声明处声明一次即可,在函数定义时不可再重复声明,会报错!!! 31 std::string dec2n(int x, int n) 32 { 33 using namespace std; 34 35 string s; 36 char c; 37 int k; 38 39 // 若出现 x = 0 的情况 40 // 必须添加,否则会返回空字符串 41 if (x == 0) 42 { 43 return "0"; // 返回字符串0 44 } 45 46 while (x != 0) 47 { 48 k = x % n; 49 if (k > 9) // 字母范畴 50 { 51 c = 'A' + (k - 10); 52 s += c; 53 } 54 else // 数字范畴 55 { 56 c = '0' + k; 57 s += c; 58 } 59 x /= n; 60 } 61 62 string str(s.size(), ' '); 63 reverse_copy(s.begin(), s.end(), str.begin()); // 反转后返回 64 65 return str; 66 }
运行测试截图

实验任务6
源代码task6.cpp
1 // 打印字母密文对照表 2 // 输出格式控制、大小写转换、循环移位 3 4 #include <iostream> 5 #include <vector> 6 #include <iomanip> 7 #include <algorithm> 8 9 void output(); 10 11 int main() 12 { 13 output(); 14 return 0; 15 } 16 17 void output() 18 { 19 using namespace std; 20 21 vector<char> v; 22 23 // 添加元素 24 for (int i = 0; i < 26; i++) 25 { 26 char c; 27 c = 'a' + i; 28 v.push_back(c); 29 } 30 31 // 第一行输出 32 cout << " "; 33 for (auto &c : v) 34 { 35 cout << setw(2) << c; 36 c = toupper(c); // 小写转换成大写 37 } 38 cout << endl; 39 40 for (int i = 1; i <= 26; i++) 41 { 42 // 循环左移1位 43 rotate(v.begin(), v.begin() + 1, v.end()); 44 cout << setw(2) << i; 45 for (auto &c : v) 46 { 47 cout << setw(2) << c; 48 } 49 cout << endl; 50 } 51 }
运行测试截图

实验任务7
源代码task7.cpp
1 // 自动生成小学生算术运算题目并自动测评 2 // 题目是减法运算时,要求第一个操作数>=第二个操作数 3 // 题目是除法运算时,要求第一个操作数能整除第二个操作数整除 4 // 给出正确率,要求以百分号形式输出,保留小数点后两位 5 6 #include <iostream> 7 #include <iomanip> 8 #include <cstdlib> 9 #include <ctime> 10 11 char generate_random_operator(); 12 void generate_random_number(int &m, int &n); 13 int calculate(const int m, const int n, char c); 14 15 int main() 16 { 17 std::srand(std::time(0)); // 设置随机数种子 18 19 int k = 0, ans, count = 0; 20 while (k < 10) 21 { 22 int m, n; 23 generate_random_number(m, n); 24 char c = generate_random_operator(); 25 26 // 判断减法是否合法 27 if (c == '-') 28 { 29 if (m < n) 30 continue; 31 } 32 33 // 判断除法是否合法 34 else if (c == '/') 35 { 36 if (m % n != 0) 37 continue; 38 } 39 40 k++; 41 std::cout << m << ' ' << c << ' ' << n << " = "; 42 std::cin >> ans; 43 44 if (calculate(m, n, c) == ans) 45 count++; 46 } 47 48 std::cout << "正确率: "; 49 double rate = count * 100.0 / 10.0; 50 std::cout << std::fixed << std::setprecision(2) << rate << '%' << std::endl; 51 52 return 0; 53 } 54 55 // 生成随机运算符 56 char generate_random_operator() 57 { 58 switch (rand() % 4) 59 { 60 case 0: 61 return '+'; 62 break; 63 case 1: 64 return '-'; 65 break; 66 case 2: 67 return '*'; 68 break; 69 default: 70 return '/'; 71 } 72 } 73 74 // 生成随机运算数 75 void generate_random_number(int &m, int &n) 76 { 77 m = rand() % 10 + 1; 78 n = rand() % 10 + 1; 79 } 80 81 // 计算正确结果 82 int calculate(const int m, const int n, char c) 83 { 84 if (c == '+') 85 return m + n; 86 else if (c == '-') 87 return m - n; 88 else if (c == '*') 89 return m * n; 90 else 91 return m / n; 92 }
运行测试截图

实验总结
- 了解并实现了常见算法函数的使用:如 reverse_copy(),rotate(),tolower(),toupper() 以及生成随机数的方法等
- 注意:带有默认形参的函数只需在声明时声明一次即可,在定义时不可再声明,会导致报错
- tolower 和 toupper 的形参只能是字符型,若填入 string 类型会报错
- 用 std::getline() 读取包含空格的整行文本

浙公网安备 33010602011771号