实验1 现代C++编程初体验
任务1:
源代码task1.cpp
1 //现代C++标准库、算法库体验 2 //本例用到以下内容: 3 // 1.字符串string,动态数组容器类vector、迭代器 4 // 2.算法库:反转元素次序、旋转元素 5 // 3.函数模板、const引用作为形参 6 7 #include<iostream> 8 #include<string> 9 #include<vector> 10 #include<algorithm> 11 12 //模板函数声明 13 template<typename T> 14 void output(const T &c); 15 16 void test1(); 17 void test2(); 18 void test3(); 19 20 int main() { 21 std::cout<<"测试1:\n"; 22 test1(); 23 24 std::cout<<"\n测试2:\n"; 25 test2(); 26 27 std::cout<<"\n测试3:\n"; 28 test3(); 29 } 30 31 //输出容器对象c中的元素 32 template<typename T> 33 void output(const T &c) { 34 for(auto &i:c) 35 std::cout <<i<<' '; 36 std::cout<<"\n"; 37 } 38 39 //测试1:组合使用算法库、迭代器、string反转字符串 40 void test1(){ 41 using namespace std; 42 43 string s0{"0123456789"}; 44 cout<<"s0="<<s0<<endl; 45 46 string s1(s0); 47 //反转s1自身 48 reverse(s1.begin(),s1.end()); 49 cout<<"s1="<<s1<<endl; 50 51 string s2(s0.size(),' '); 52 //将s0反转后结果拷贝到s2,s0自身不变 53 reverse_copy(s0.begin(),s0.end(),s2.begin()); 54 cout<<"s2="<<s2<<endl; 55 } 56 57 //测试2:组合使用算法库、迭代器、vector反转动态数组对象vector内数据 58 void test2(){ 59 using namespace std; 60 61 vector<int> v0{2,0,4,9}; 62 cout<<"v0:";output(v0); 63 64 vector<int> v1{v0}; 65 reverse(v1.begin(),v1.end()); 66 cout<<"v1:";output(v1); 67 68 vector<int> v2{v0}; 69 reverse_copy(v0.begin(),v0.end(),v2.begin()); 70 cout<<"v2:";output(v2); 71 } 72 73 //测试3:组合使用算法库、迭代器、vector实现元素旋转移位 74 void test3(){ 75 using namespace std; 76 77 vector<int> v0{0,1,2,3,4,5,6,7,8,9}; 78 cout<<"v0: ";output(v0); 79 80 vector<int> v1{v0}; 81 //将[v1.begin(),v1.end())区间内元素循环左移1位 82 rotate(v1.begin(),v1.begin()+1,v1.end()); 83 cout<<"v1: ";output(v1); 84 85 vector<int> v2{v0}; 86 //将[v1.begin(),v1.end())区间内元素循环左移2位 87 rotate(v2.begin(),v2.begin()+2,v2.end()); 88 cout<<"v2: ";output(v2); 89 90 vector<int> v3{v0}; 91 //将[v1.begin(),v1.end())区间内元素循环右移1位 92 rotate(v3.begin(),v3.end()-1,v3.end()); 93 cout<<"v3: ";output(v3); 94 95 vector<int> v4{v0}; 96 //将[v1.begin(),v1.end())区间内元素循环右移2位 97 rotate(v4.begin(),v4.end()-2,v4.end()); 98 cout<<"v4: ";output(v4); 99 }
运行结果截图:
问题1
reverse 直接反转原序列(修改原序列); reverse_copy 反转原序列后复制到新序列(不修改原序列)。
问题2
rotate 通过将“起始到分界点”的元素移到末尾、“分界点到结束”的元素移到开头来改变顺序。三个参数分别是:序列起始迭代器、分界点迭代器、序列结束迭代器。
任务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 //模板函数声明 10 template<typename T> 11 void output(const T &c); 12 13 int generate_random_number(); 14 void test1(); 15 void test2(); 16 17 int main(){ 18 std::srand(std::time(0)); //添加随机种子 19 std::cout <<"测试1:\n"; 20 test1(); 21 22 std::cout<<"\n测试2:\n"; 23 test2(); 24 } 25 26 //输出容器对象c中的元素 27 template<typename T> 28 void output(const T &c){ 29 for(auto &i:c) 30 std::cout<<i<<' '; 31 std::cout<<'\n'; 32 } 33 34 //返回[0,100]区间内的一个随机整数 35 int generate_random_number(){ 36 return std::rand()%101; 37 } 38 39 //测试1:对容器类对象指定迭代器区间赋值、排序 40 void test1(){ 41 using namespace std; 42 43 vector<int> v0(10);//创建一个动态数组对象v0,对象大小为10 44 generate(v0.begin(),v0.end(),generate_random_number);//生成随机数填充v0 45 cout<<"v0:";output(v0); 46 47 vector<int> v1{v0}; 48 sort(v1.begin(),v1.end());//对整个vector排序 49 cout<<"v1:";output(v1); 50 51 vector<int> v2{v0}; 52 sort(v2.begin()+1,v2.end()-1);//只对中间部分排序,不包含首尾元素 53 cout<<"v2:";output(v2); 54 } 55 56 //测试2:对容器类对象指定迭代器区间赋值、计算最大值/最小值/均值 57 void test2(){ 58 using namespace std; 59 60 vector<int> v0(10); 61 generate(v0.begin(),v0.end(),generate_random_number); 62 cout<<"v0:";output(v0); 63 64 //求最大值和最小值 65 auto min_iter=min_element(v0.begin(),v0.end()); 66 auto max_iter=max_element(v0.begin(),v0.end()); 67 cout<<"最小值:"<< *min_iter<<endl; 68 cout<<"最大值:"<< *max_iter<<endl; 69 70 //同时求最大值和最小值 71 auto ans=minmax_element(v0.begin(),v0.end()); 72 cout<<"最小值:"<< *(ans.first)<<endl; 73 cout<<"最大值:"<< *(ans.second)<<endl; 74 75 //求平均值 76 double avg1=accumulate(v0.begin(),v0.end(),0.0)/v0.size(); 77 cout<<"均值:"<<fixed<<setprecision(2)<<avg1<<endl; 78 79 sort(v0.begin(),v0.end()); 80 double avg2=accumulate(v0.begin()+1,v0.end()-1,0.0)/(v0.size()-2); 81 cout<<"去掉最大值、最小值之后,均值:"<<avg2<<endl; 82 }
运行结果截图:
问题1
generate 算法的作用是利用指定的生成函数,为指定范围内的每个元素生成新值,以此填充或覆盖该范围内的元素。
问题2
minmax_element 相比分别调用 min_element 和 max_element ,优势在于只需遍历一次序列就能同时找到最小值和最大值的迭代器,效率更高;且代码更简洁,一行代码即可同时获取两者相关信息。
问题3
lambda 表达式的适用场景为:需要在调用 generate 处直接定义生成逻辑,追求代码简洁紧凑,无需单独定义函数;生成逻辑为局部性的,不希望污染全局命名空间;或者需要灵活捕获外部变量(即使此例未捕获,也具备该潜力)的情况。
任务3:
源代码task3.cpp
1 #include<iostream> 2 #include<string> 3 #include<algorithm> 4 #include<cctype> 5 6 unsigned char func(unsigned char c); 7 void test1(); 8 void test2(); 9 10 int main(){ 11 std::cout<<"测试1:字符串大小写转换\n"; 12 test1(); 13 14 std::cout<<"\n测试2:字符变换\n"; 15 test2(); 16 } 17 18 unsigned char func(unsigned char c){ 19 if(c=='z') 20 return 'a'; 21 22 if(c=='Z') 23 return 'A'; 24 25 if(std::isalpha(c)) 26 return static_cast<unsigned char>(c+1); 27 28 return c; 29 } 30 31 void test1(){ 32 std::string s1{"Hello World 2049!"}; 33 std::cout<<"s1="<<s1<<'\n'; 34 35 std::string s2; 36 for(auto c: s1) 37 s2+=std::tolower(c); 38 std::cout<<"s2="<<s2<<'\n'; 39 40 std::string s3; 41 for(auto c: s1) 42 s3+=std::toupper(c); 43 std::cout<<"s3="<<s3<<'\n'; 44 } 45 46 void test2(){ 47 std::string s1{"I love cosmos!"}; 48 std::cout<<"s1="<<s1<<'\n'; 49 50 std::string s2(s1.size(),' '); 51 std::transform(s1.begin(),s1.end(), 52 s2.begin(), 53 func); 54 std::cout<<"s2="<<s2<<'\n'; 55 }
运行结果截图:
问题1
func 功能是将当前字母转换为后一个字母
问题2
tolower 将大写字母转小写; toupper 将小写字母转大写。
问题3
transform 四参数(以 transform(first1, last1, first2, result, op) 为例): first1 / last1 是第一个输入序列范围, first2 是第二个输入序列起始, result 是结果序列起始;把 参数 s2.begin() 改成 s1.begin() ,会改变第二个输入序列的元素来源,可能改变结果。
任务4:
源代码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 using namespace std; 10 string s; 11 12 //多组输入,直到按下Ctrl+Z结束测试 13 while(cin>>s){ 14 cout<<boolalpha 15 <<"区分大小写:"<<is_palindrome(s)<<"\n" 16 <<"不区分大小写:"<<is_palindrome_ignore_case(s)<<"\n\n"; 17 } 18 } 19 20 21 //函数is_palindrome定义 22 bool is_palindrome(const std::string &s){ 23 int left=0,right=s.size()-1; 24 while(left<right){ 25 if(s[left]!=s[right]){ 26 return false; 27 } 28 left++; 29 right--; 30 } 31 return true; 32 } 33 //函数is_palindrome_ignore_case定义 34 bool is_palindrome_ignore_case(const std::string &s){ 35 //将字符串转换为小写 36 std::string s2; 37 for(auto c: s) 38 s2+=std::tolower(c); 39 int left=0,right=s2.size()-1; 40 while(left<right){ 41 if(s2[left]!=s2[right]){ 42 return false; 43 } 44 left++; 45 right--; 46 } 47 return true; 48 }
运行结果截图:
cin >> s 无法读含空格的字符串,需改用 getline(cin, s) (可读取整行含空格内容,若之前有 cin 输入需先加 cin.ignore() 处理换行)。
任务5:
源代码task5.cpp
1 #include<iostream> 2 #include<string> 3 #include<algorithm> 4 5 std::string dec2n(int x,int n=2); 6 7 int main(){ 8 int x; 9 while(std::cin>>x){ 10 std::cout<<"十进制:"<<x<<'\n' 11 <<"二进制:"<<dec2n(x)<<'\n' 12 <<"八进制:"<<dec2n(x,8)<<'\n' 13 <<"十二进制:"<<dec2n(x,12)<<'\n' 14 <<"十六进制:"<<dec2n(x,16)<<'\n' 15 <<"三十二进制:"<<dec2n(x,32)<<'\n'; 16 } 17 } 18 19 //函数dec2n定义 20 std::string dec2n(int x,int n){ 21 //处理x为0的特殊情况 22 if(x==0){ 23 return "0"; 24 } 25 std::string result; 26 while(x>0){ 27 int remainder=x%n; 28 char c; 29 //余数小于10时,直接转换为对应的数字字符 30 if(remainder<10){ 31 c='0'+remainder; 32 } 33 else{ 34 //余数大于等于10时,转换为对应的大写字母(A-Z) 35 c='A'+remainder-10; 36 } 37 result.push_back(c); 38 //继续处理商 39 x/=n; 40 } 41 //因为取余是从低位到高位生成字符,所以需要反转得到正确顺序 42 std::reverse(result.begin(),result.end()); 43 return result; 44 }
运行结果截图:
任务6:
源代码task6.cpp
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<iomanip>//用于setw格式化 5 using namespace std; 6 7 int main(){ 8 char plain[]="abcdefghijklmnopqrstuvwxyz";//明文字母表 9 int len=strlen(plain); 10 //输出首行明文,带空格 11 cout<<setw(2)<<" ";//行号占位 12 for(int j=0;j<len;j++){ 13 cout<<setw(2)<<plain[j]; 14 } 15 cout<<endl; 16 for(int i=1;i<=26;i++){ 17 char cipher[27]; 18 strcpy(cipher,plain);//复制明文到密文数组 19 //对cipher执行循环右移i位:将前i个元素移到末位 20 rotate(cipher,cipher+i,cipher+len); 21 //输出行号和大写密文 22 cout<<setw(2)<<i; 23 for(int j=0;j<27;j++){ 24 cout<<setw(2)<<(char)(cipher[j]-32);//ASCII码'a'为97'A'为65,小写变大写 25 } 26 cout<<endl; 27 } 28 return 0; 29 }
运行结果截图:
任务7:
源代码task7.cpp
1 #include<iostream> 2 #include<cstdlib> 3 #include<ctime> 4 #include<iomanip> 5 using namespace std;//不使用的话cout应表示成std::cout 6 int main(){ 7 //初始化随机数种子,使每次运行随机数不同 8 srand(static_cast<unsigned int>(time(0))); 9 int correctCount=0; 10 for(int i=0;i<10;i++){ 11 //随机生成两个1到10的操作数 12 int num1=rand()%10+1; 13 int num2=rand()%10+1; 14 //随机生成运算符号(0:加,1:减,2:乘,3:除) 15 int op=rand()%4; 16 int result; 17 char opChar; 18 //根据不同运算符号处理 19 switch(op){ 20 case 0: //加法 21 result=num1+num2; 22 opChar='+'; 23 break; 24 case 1: //减法:确保num1>=num2 25 while(num1<num2){ 26 num1=rand()%10+1; 27 num2=rand()%10+1; 28 } 29 result=num1-num2; 30 opChar='-'; 31 break; 32 case 2: //乘法 33 result=num1*num2; 34 opChar='*'; 35 break; 36 case 3: //除法:确保num1能整除num2 37 while(num2==0||num1%num2!=0){ 38 num1=rand()%10+1; 39 num2=rand()%10+1; 40 } 41 result=num1/num2; 42 opChar='/'; 43 break; 44 } 45 //输出题目,提示用户输入答案 46 cout<<num1<<" "<<opChar<<" "<<num2<<"="; 47 int userAnswer; 48 cin>>userAnswer; 49 //判断答案是否正确,正确则统计 50 if(userAnswer==result){ 51 correctCount++; 52 } 53 } 54 //计算并输出正确率,保留两位小数 55 double accuracy=static_cast<double>(correctCount)/10*100; 56 cout<<"正确率:"<<fixed<<setprecision(2)<<accuracy<<"%"<<endl; 57 return 0; 58 }
运行结果截图: