实验1 现代C++编程初体验
任务一:
源代码task1.cpp
1 #include<iostream> 2 #include<string> 3 #include<vector> 4 #include<algorithm> 5 template<typename T> 6 void output(const T&c); 7 void test1(); 8 void test2(); 9 void test3(); 10 int main() 11 { 12 std::cout<<"测试1:\n"; 13 test1(); 14 std::cout<<"\n测试2:\n"; 15 test2(); 16 std::cout<<"\n测试3:\n"; 17 test3(); 18 } 19 template<typename T> 20 void output(const T&c) 21 { 22 for(auto &i:c) 23 std::cout<<i<<' '; 24 std::cout<<'\n'; 25 } 26 void test1() 27 { 28 using namespace std; 29 string s0{"0123456789"}; 30 cout<<"s0="<<s0<<endl; 31 32 string s1(s0); 33 reverse(s1.begin(),s1.end()); 34 cout<<"s1="<<s1<<endl; 35 36 string s2(s0.size(),' '); 37 reverse_copy(s0.begin(),s0.end(),s2.begin()); 38 cout<<"s2="<<s2<<endl; 39 } 40 void test2() 41 { 42 using namespace std; 43 vector<int> v0{2,0,4,9}; 44 cout<<"v0:"; 45 output(v0); 46 47 vector<int> v1{v0}; 48 reverse(v1.begin(),v1.end()); 49 cout<<"v1:"; 50 output(v1); 51 52 vector<int> v2{v0}; 53 reverse_copy(v0.begin(),v0.end(),v2.begin()); 54 cout<<"v2:"; 55 output(v2); 56 } 57 void test3() 58 { 59 using namespace std; 60 vector<int> v0{0,1,2,3,4,5,6,7,8,9}; 61 cout<<"v0:"; 62 output(v0); 63 64 vector<int> v1{v0}; 65 rotate(v1.begin(),v1.begin()+1,v1.end()); 66 cout<<"v1:"; 67 output(v1); 68 69 vector<int> v2{v0}; 70 rotate(v2.begin(),v2.begin()+2,v2.end()); 71 cout<<"v2:"; 72 output(v2); 73 74 vector<int> v3{v0}; 75 rotate(v3.begin(),v3.end()-1,v3.end()); 76 cout<<"v3:"; 77 output(v3); 78 79 vector<int> v4{v0}; 80 rotate(v4.begin(),v4.end()-2,v4.end()); 81 cout<<"v4:"; 82 output(v4); 83 }
运行结果图:
观察与思考:
1. reverse 和 reverse_copy 有什么区别?
①reverse是改变原始序列,将数组指定范围的元素顺序调转
②reverse_copy是将原始序列指定范围的元素顺序调转 并 存储到指定序列位置后,不改变原始序列
2. rotate 算法是如何改变元素顺序的?它的三个参数分别代表什么?
①rotate 算法通过“左旋转”改变元素顺序:将 [first, middle) 范围内的元素移到 [middle, last) 之后,形成 [middle, last) + [first, middle) 的新顺序。
②三个参数: first:序列起始迭代器(包含) middle:旋转点迭代器(以此为界分割元素) last:序列结束迭代器(不包含)
任务二:
源代码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 11 void output(const T &c); 12 int generate_random_number(); 13 void test1(); 14 void test2(); 15 16 int main() 17 { 18 std::srand(std::time(0)); // 添加随机种子 19 20 std::cout << "测试1: \n"; 21 test1(); 22 23 std::cout << "\n测试2: \n"; 24 test2(); 25 } 26 27 // 输出容器对象c中的元素 28 template <typename T> 29 30 void output(const T &c) 31 { 32 for(auto &i: c) 33 std::cout << i << ' '; 34 std::cout << '\n'; 35 } 36 37 // 返回[0, 100]区间内的一个随机整数 38 int generate_random_number() 39 { 40 return std::rand() % 101; 41 } 42 43 // 测试1:对容器类对象指定迭代器区间赋值、排序 44 void test1() 45 { 46 using namespace std; 47 48 vector<int> v0(10); // 创建一个动态数组对象v0, 对象大小为10 49 generate(v0.begin(), v0.end(), generate_random_number); // 生成随机数填充v0 50 cout << "v0: "; output(v0); 51 52 vector<int> v1{v0}; 53 sort(v1.begin(), v1.end()); // 对整个vector排序 54 cout << "v1: "; output(v1); 55 56 vector<int> v2{v0}; 57 sort(v2.begin()+1, v2.end()-1); // 只对中间部分排序,不包含首尾元素 58 cout << "v2: "; output(v2); 59 } 60 61 // 测试2:对容器类对象指定迭代器区间赋值、计算最大值/最小值/均值 62 void test2() 63 { 64 using namespace std; 65 66 vector<int> v0(10); 67 generate(v0.begin(), v0.end(), generate_random_number); 68 cout << "v0: "; output(v0); 69 70 // 求最大值和最小值 71 auto min_iter = min_element(v0.begin(), v0.end()); 72 auto max_iter = max_element(v0.begin(), v0.end()); 73 cout << "最小值: " << *min_iter << endl; 74 cout << "最大值: " << *max_iter << endl; 75 76 // 同时求最大值和最小值 77 auto ans = minmax_element(v0.begin(), v0.end()); 78 cout << "最小值: " << *(ans.first) << endl; 79 cout << "最大值: " << *(ans.second) << endl; 80 81 // 求平均值 82 double avg1 = accumulate(v0.begin(), v0.end(), 0.0) / v0.size(); 83 cout << "均值: " << fixed << setprecision(2) << avg1 << endl; 84 sort(v0.begin(), v0.end()); 85 86 double avg2 = accumulate(v0.begin()+1, v0.end()-1, 0.0) / (v0.size()-2); 87 cout << "去掉最大值、最小值之后,均值: " << avg2 << endl; 88 }
运行结果图:
观察与思考:
1. generate 算法的作用是什么?
通过调用一个函数生成值(源代码中的generate_random_number()),为指定范围(源代码中v0.begin(), v0.end())内的元素逐一赋值。
2. minmax_element和分别调用min_element、max_element相比,有什么优势?
前者一次遍历即可同时获取最大和最小值,比分别调用后者效率更高。
3. 把代码中函数generate_random_number的声明(line13)和定义(line35-37)注释起来,把两处调用改成如下写法,观察效果是否等同?查阅c++中lambda表达式用法。
效果等同,但是lambda 表达式是匿名函数,可以直接定义在代码中,简洁方便。
任务三:
源代码task3:
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <cctype> 5 6 unsigned char func(unsigned char c); 7 8 void test1(); 9 void test2(); 10 11 int main() 12 { 13 std::cout << "测试1: 字符串大小写转换\n"; 14 test1(); 15 16 std::cout << "\n测试2: 字符变换\n"; 17 test2(); 18 } 19 20 unsigned char func(unsigned char c) 21 { 22 if(c == 'z')return 'a'; 23 if(c == 'Z')return 'A'; 24 if(std::isalpha(c)) 25 return static_cast<unsigned char>(c+1); 26 return c; 27 } 28 29 void test1() 30 { 31 std::string s1{"Hello World 2049!"}; 32 std::cout << "s1 = " << s1 << '\n'; 33 34 std::string s2; 35 36 for(auto c: s1) 37 s2 += std::tolower(c); 38 std::cout << "s2 = " << s2 << '\n'; 39 40 std::string s3; 41 42 for(auto c: s1) 43 s3 += std::toupper(c); 44 std::cout << "s3 = " << s3 << '\n'; 45 } 46 47 void test2() 48 { 49 using namespace std; 50 51 string s1{"I love cosmos!"}; 52 cout << "s1 = " << s1 << '\n'; 53 54 string s2(s1.size(), ' '); 55 transform(s1.begin(), s1.end(),s2.begin(),func); 56 cout << "s2 = " << s2 << '\n'; 57 }
运行结果图:
观察与思考:
1. 自定义函数func功能是什么?
将指定元素字母转换成下一个字母,当到达'z''Z'时返回到'a''A'。
2. tolower和toupper功能分别是什么?
①tolower 将序列中所有的字母元素全部转换成小写字母
②toupper 将序列中所有的字母元素全部转换成大写字母
3. transform的4个参数意义分别是什么?如果把第3个参数s2.begin()改成s1.begin(),有何区别?
参数含义: ①s1.begin()代表转换操作的起始迭代器
②s1.end()代表转换操作的结束迭代器(不包含)
③s2.begin()代表输出序列的起始迭代器
④func 是对于输入范围内的元素的处理操作函数
会直接赋值到s1本身,改变原始序列。
任务四:
源代码task4.cpp
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 5 bool is_palindrome(const std::string &s); 6 bool is_palindrome_ignore_case(const std::string &s); 7 8 int main() 9 { 10 using namespace std; 11 12 string s; 13 while(cin>>s) 14 { 15 cout<<boolalpha 16 << "区分大小写: " << is_palindrome(s) << "\n" 17 << "不区分大小写: " << is_palindrome_ignore_case(s) 18 << "\n\n"; 19 } 20 } 21 22 bool is_palindrome(const std::string &s) 23 { 24 using namespace std; 25 26 string s1(s.size(),' '); 27 reverse_copy(s.begin(),s.end(),s1.begin()); 28 29 if(s==s1)return true; 30 else return false; 31 } 32 33 bool is_palindrome_ignore_case(const std::string &s) 34 { 35 using namespace std; 36 37 string s1; 38 for(auto c:s) 39 s1+=toupper(c); 40 41 string s2(s.size(),' '); 42 reverse_copy(s1.begin(),s1.end(),s2.begin()); 43 44 if(s1==s2)return true; 45 else return false; 46 }
运行结果截图:
观察与思考:
使用cin >> s输入时,输入的字符串中不能包含空格。如果希望测试字符串包含空格(如hello oop),代码应如何调整
使用getline(cin,s)代替cin>>s。
任务五:
源代码task5.cpp
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 using namespace std; 5 6 string dec2n(int x, int n = 2); 7 8 int main() 9 { 10 int x; 11 while(cin >> x) { 12 cout << "十进制: " << x << '\n' 13 << "二进制: " << dec2n(x) << '\n' 14 << "八进制: " << dec2n(x, 8) << '\n' 15 << "十二进制: " << dec2n(x, 12) << '\n' 16 << "十六进制: " << dec2n(x, 16) << '\n' 17 << "三十二进制: " << dec2n(x, 32) << "\n\n"; 18 } 19 } 20 21 string dec2n(int x, int n ) 22 { 23 string result; 24 int t; 25 26 if(x==0)return "0"; 27 28 while(x>0) 29 { 30 t=x%n; 31 if(t<=9) result+=char(t+'0'); 32 else 33 result+=char('A'+t-10); 34 x=x/n; 35 } 36 reverse(result.begin(),result.end()); 37 38 return result; 39 }
运行结果截图:
任务六:
源代码task6.cpp
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <vector> 5 using namespace std; 6 7 int main() 8 { 9 vector<char> s0{'a','b','c','d','e','f','g','h','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'}; 10 cout<<' '<<' '; 11 12 for(auto ch:s0) 13 cout<<' '<<ch; 14 cout<<endl; 15 16 int i=1; 17 for(auto &c:s0) 18 c=(char)toupper(c); 19 20 while(i<=26) 21 { 22 if(i<=9) cout<<' '; 23 cout<<i<<' '; 24 rotate(s0.begin(),s0.begin()+1,s0.end()); 25 for(auto ch : s0) 26 cout<<ch<<' '; 27 cout<<endl; 28 i++; 29 } 30 return 0; 31 }
运行结果截图:
任务七:
源代码task7.cpp
1 #include <iostream> 2 #include <cstdlib> 3 #include <ctime> 4 #include <iomanip> 5 6 using namespace std; 7 8 int main() 9 { 10 srand(time(0)); 11 12 int correct = 0; 13 const int total = 10; 14 15 for (int i = 0; i < total; i++) 16 { 17 int a = rand() % 10 + 1; 18 int b = rand() % 10 + 1; 19 int op = rand() % 4; 20 21 if (op == 1) 22 if (a < b) 23 swap(a, b); 24 else if (op == 3) 25 { 26 if(a%b!=0) 27 { 28 int multiple = rand() % 10 + 1; 29 a = b * multiple; 30 31 if (a > 10) 32 a = b; 33 } 34 } 35 int answer; 36 char op_char; 37 switch (op) 38 { 39 case 0: 40 answer = a + b; 41 op_char = '+'; 42 break; 43 case 1: 44 answer = a - b; 45 op_char = '-'; 46 break; 47 case 2: 48 answer = a * b; 49 op_char = '*'; 50 break; 51 case 3: 52 answer = a / b; 53 op_char = '/'; 54 break; 55 } 56 cout << a << " " << op_char << " " << b << " = "; 57 int user_answer; 58 cin >> user_answer; 59 60 if (user_answer == answer) 61 correct++; 62 } 63 64 double rate = (double)correct / total * 100; 65 cout << "正确率: " << fixed << setprecision(2) << rate << "%" << endl; 66 67 return 0; 68 }
运行结果截图:
实验总结:
1.学会了如reverse generate rotate等算法高效处理数据元素,提升效率,增强代码可读性。
2.巩固了vector 等已经学过的知识,通过撰写代码提升了逻辑思维能力和编写代码能力。