实验一 现代C++编程初体验


实验任务一
源代码:

 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     std::cout << "测试1: \n";
12     test1();
13     std::cout << "测试2: \n";
14     test2();
15     std::cout << "测试3: \n";
16     test3();
17 }
18 
19 template<typename T>
20 void output(const T& c) {
21     for (auto& i : c)
22         std::cout << i << ' ';
23     std::cout << '\n' ;
24 }
25 void test1() {
26     using namespace std;
27 
28     string s0{ "0123456789" };
29     cout << "s0 = " << s0 << endl;
30     string s1(s0);
31     reverse(s1.begin(), s1.end());
32     cout << "s1 = " << s1 << endl;
33 
34     string s2(s0.size(), ' ');
35     reverse_copy(s0.begin(), s0.end(), s2.begin());
36     cout << "s2 = " << s2 << endl;    
37 }
38 void test2() {
39     using namespace std;
40 
41     vector<int> v0{ 2,0,4,9 };
42     cout << "v0: "; output(v0);
43     vector<int> v1{ v0 };
44     reverse(v1.begin(), v1.end());
45     cout << "v1: "; output(v1);
46 
47     vector<int> v2{ v0 };
48     reverse_copy(v0.begin( ), v0.end( ), v2.begin( ));
49     cout << "v2: "; output(v2);
50 
51 }
52 void test3() {
53     using namespace std;
54 
55     vector<int> v0{ 0,1,2,3,4,5,6,7,8,9 };
56     cout << "v0: "; output(v0);
57 
58     vector<int> v1{ v0 };
59     rotate(v1.begin(), v1.begin() + 1, v1.end());
60     cout << "v1: "; output(v1);
61 
62     vector<int> v2{ v0 };
63     rotate(v2.begin(), v2.begin() + 2, v2.end());
64     cout << "v2: "; output(v2);
65 
66     vector<int> v3{ v0 };
67     rotate(v3.begin(), v3.end() - 1, v3.end());
68     cout << "v3: "; output(v3);
69     vector<int> v4{ v0 };
70     rotate(v4.begin(), v4.end() - 2, v4.end());
71     cout << "v4: "; output(v4);
72 }
View Code

运行测试截图:

a2ba9fb1-2c27-42cc-b59b-1dca22e4ba02

观察与思考:

1.reverse 和 reverse_copy 有什么区别?

  reverse 会直接在原字符串上直接进行反转操作,改变了原容器内容。

  reverse_copy 则会把反转后的结果复制到另一个容器中,原容器保持不变。

2.rotate 算法是如何改变元素顺序的?它的三个参数分别代表什么?

  功能:将范围为 [first, last)的以 middle 为分割点,将 [first, middle) 和 [middle, last) 两部分交换顺序,形成范围为 [middle, last) + [first, middle)的新字符串。

  三个参数:

    first:范围的起始迭代器;

    middle:旋转的分割点迭代器,即旋转后此位置的元素将成为首元素;

    last:范围的结束迭代器。

实验任务二

 源代码:

 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     
12 int generate_random_number();
13     void test1();
14     void test2();
15 
16      int main() {
17         std::srand(std::time(0));
18         std::cout << "测试1: \n";
19         test1();
20      
21         std::cout << "\n测试2: \n";
22         test2();
23      }
24     
25      template <typename T>
26      void output(const T &c) {
27     for(auto &i : c)
28             std::cout << i << ' ';
29        std::cout << '\n';
30      }
31     
32      int generate_random_number() {
33      return std::rand() % 101;
34  }
35      
36      void test1() {
37          using namespace std;
38      
39          vector<int> v0(10);
40      generate(v0.begin(), v0.end(), generate_random_number);
41        cout << "v0: "; output(v0);
42 
43         vector<int> v1{v0};
44      sort(v1.begin(), v1.end());
45          cout << "v1: "; output(v1);
46          vector<int> v2{v0};
47         sort(v2.begin()+1, v2.end()-1);
48      cout << "v2: "; output(v2);
49  }
50      void test2() {
51          using namespace std;
52  
53      vector<int> v0(10);
54      generate(v0.begin(), v0.end(), generate_random_number);
55      cout << "v0: "; output(v0);
56  
57      auto min_iter = min_element(v0.begin(), v0.end());
58      auto max_iter = max_element(v0.begin(), v0.end());
59      cout << "最小值: " << *min_iter << endl;
60  
61     auto ans = minmax_element(v0.begin(), v0.end());
62     cout << "最小值: " << *(ans.first) << endl;
63     cout << "最大值: " << *(ans.second) << endl;
64 
65     double avg1 = accumulate(v0.begin(), v0.end(), 0.0) / v0.size();
66     cout << "均值: " << fixed << setprecision(2) << avg1 << endl;
67 
68    sort(v0.begin(), v0.end());
69       double avg2 = accumulate(v0.begin()+1, v0.end()-1, 0.0) / (v0.size()-2);
70          cout << "去掉最大值、最小值之后,均值: " << avg2 << endl;
71 }
View Code

运行结果截图:

784133e1-17a8-4c0c-8257-e6124835a383

观察与思考:

1.generate 算法的作用是什么?

  generate 算法的核心是 “按照某种逻辑或规则创建内容”。在任务二中,用生成指定范围和数据元素的vector容器。

2.minmax_element 和分别调用 min_element 、 max_element 相比,有什么优势?

     通过一次遍历,完成了两次操作。减少了时间开销,使代码更加简洁。

3.把代码中函数 generate_random_number 的声明(line13)和定义(line35-37)注释起来,把两处调用改成如 下写法,观察效果是否等同?查阅c++中lambda表达式用法。

  效果等同。C++ lambda表达式用法:lambda表达式是一种匿名函数,语法格式为 [捕获列表] (参数列表) -> 返回值类型 { 函数体 } 。其中捕获列表用于指定外部变量的访问方式(如 [] 空捕获、 [&] 引用捕获、 [=] 值捕获等);参数列表和普通函数类似;返回值类型可省略,由编译器自动推导。示例中的lambda [](){return std::rand()%101;} 属于无捕获、无参数、返回 int 类型的简单形式。lambda可以直接在使用的地方定义,避免单独定义命名函数的麻烦。

实验任务三

源代码:

#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
unsigned char func(unsigned char c);
void test1();
void test2();
int main() {
    std::cout << "测试1: 字符串大小写转换\n";
    test1();
    std::cout << "\n测试2: 字符变换\n";
    test2();
}
unsigned char func(unsigned char c) {
    if(c == 'z')
        return 'a';
    
    if(c == 'Z')
        return 'A';
    
    if(std::isalpha(c))
        return static_cast<unsigned char>(c+1);
    
    return c;
}
void test1() {
    std::string s1{"Hello World 2049!"};
    std::cout << "s1 = " << s1 << '\n';
    std::string s2;
    for(auto c: s1)
        s2 += std::tolower(c);
    std::cout << "s2 = " << s2 << '\n'; 
    std::string s3;
    for(auto c: s1)
        s3 += std::toupper(c);
    std::cout << "s3 = " << s3 << '\n'; 
}
void test2() {
    std::string s1{"I love cosmos!"};
    std::cout << "s1 = " << s1 << '\n';
    
    std::string s2(s1.size(), ' ');
    std::transform(s1.begin(), s1.end(),
                   s2.begin(),
                   func);
    std::cout << "s2 = " << s2 << '\n';
}
View Code

 运行测试截图:

9a2ff86a-97c4-4f94-b1b7-1f703d09c890

观察与思考:

1.自定义函数 func 功能是什么?

  将单个字母变为字母表顺序的后一字母(z变为a,Z变为A)。

 

2.tolower 和 toupper 功能分别是什么?

  tolower:将大写字母变为小写;toupper:将小写字母变为大写。非字母的字符不变。

3.transform 的4个参数意义分别是什么?如果把第3个参数 s2.begin() 改成 s1.begin() ,有何区别?

      分别为:要转换的元素的起始位置;要转换的元素的结束位置,( “左闭右开” );转换后元素的存放起始位置;一元操作函数,用于对输入范围的每个元素执行转换(接收一个输入元素,返回转换后的结果)。

    区别:改为s1.begin()后s1中的原始元素将会被覆盖。

实验任务四

源代码:

 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     using namespace std;
19     string ss(s.size(),' ');
20     reverse_copy(s.begin(),s.end(),ss.begin());
21     if(ss==s){
22         return true;
23     }
24     else{
25         return false;
26     }
27 }
28 
29 // 函数is_palindrome_ignore_case定义
30 bool is_palindrome_ignore_case(const std::string &s){
31     using namespace std;
32     string ss;
33     for(auto &i:s){
34         ss+=tolower(i);
35     }
36     string rev(s.size(),' ');
37     reverse_copy(ss.begin(),ss.end(),rev.begin());
38     if(rev==ss){
39         return true;
40     }
41     else{
42         return false;
43     }
44 }
View Code

运行结果截图:

e10e2416-5eaa-47b6-b459-37f8eda37899

观察与思考: 使用 cin >> s 输入时,输入的字符串中不能包含空格。如果希望测试字符串包含空格(如 hello oop ),代码应如何调整? 

  可以使用 std::getline 函数。

实验任务五

源代码:

 1 #include <iostream>
 2 #include <string>
 3 #include <algorithm>
 4 std::string dec2n(int x, int n = 2);
 5 int main() {
 6     int x;
 7     while(std::cin >> x) {
 8         std::cout << "十进制: " << x << '\n'
 9                   << "二进制: " << dec2n(x) << '\n'
10                   << "八进制: " << dec2n(x, 8) << '\n'
11                   << "十二进制: " << dec2n(x, 12) << '\n'
12                   << "十六进制: " << dec2n(x, 16) << '\n'
13                   << "三十二进制: " << dec2n(x, 32) << "\n\n"; 
14    }
15 }
16 // 函数dec2n定义
17 std::string dec2n(int x, int n){
18     using namespace std;
19     string ans;
20     if(x==0) return "0";
21     while(x){
22         if(x%n<=9){
23             ans+=static_cast<char>(x%n+'0');
24         }
25         else{
26             ans+=static_cast<char>('A'+(x%n-10));
27         }
28         x/=n;
29     }
30     reverse(ans.begin(),ans.end());
31     return ans;
32 }
View Code

运行测试截图:

5841d95f-9628-462d-b0d6-263036c48030

实验任务六

源代码:

 1 #include <iostream>
 2 #include <string>
 3 #include <algorithm>
 4 #include<bits/stdc++.h>
 5 using namespace std;
 6 void func(string &s){
 7     auto t=s[0];
 8     for(auto i=0;i<s.size()-1;i++){
 9         s[i]=s[i+1];
10     }
11     s[s.size()-1]=t;
12     for(auto &i:s){
13         cout<<setw(2)<<i;
14     }
15     cout<<'\n';
16 }
17 int main(){
18     string s1,s2;
19     for(char i='a';i<='z';i++){
20         s1+=i;
21     }
22     cout<<"  ";
23     for(auto i=0;i<s1.size();i++){
24         cout<<setw(2)<<s1[i];
25     }
26     cout<<'\n';
27     for(auto &i:s1){
28         s2+=toupper(i);
29     }
30     int cnt=1;
31     while(cnt<=26){
32         cout<<setw(2)<<cnt++;
33            func(s2);
34     }
35     
36     
37     return 0;
38 }
View Code

运行测试截图:

7aacd735-b118-4909-b292-ec5369879a07

实验任务七

源代码:

 1 #include <iostream>
 2 #include <string>
 3 #include <algorithm>
 4 #include<bits/stdc++.h>
 5 using namespace std;
 6 void func(string &s){
 7     auto t=s[0];
 8     for(auto i=0;i<s.size()-1;i++){
 9         s[i]=s[i+1];
10     }
11     s[s.size()-1]=t;
12     for(auto &i:s){
13         cout<<setw(2)<<i;
14     }
15     cout<<'\n';
16 }
17 int main(){
18     int ans[11],res[11];
19     srand(time(0));
20     int sign,x,y,cnt=1;
21     double r;
22     
23     while(cnt<=10){
24         sign=rand()%4;
25         if(sign==0){
26             x=rand()%9+1;
27             y=rand()%9+1;
28             cout<<x<<'+'<<y<<'=';
29             ans[cnt]=x+y;
30             cin>>res[cnt];
31         }
32         else if(sign==1){
33             do{
34                 x=rand()%9+1;
35                 y=rand()%9+1;
36             }while(x<=y);
37             cout<<x<<'-'<<y<<'=';
38             ans[cnt]=x-y;
39             cin>>res[cnt];
40         }
41         else if(sign==2){
42             x=rand()%9+1;
43             y=rand()%9+1;
44             cout<<x<<'*'<<y<<'=';
45             ans[cnt]=x*y;
46             cin>>res[cnt];
47         }
48         else{
49             do{
50                 x=rand()%9+1;
51                 y=rand()%9+1;
52             }while(x%y!=0);
53             cout<<x<<'/'<<y<<'=';
54             ans[cnt]=x/y;
55             cin>>res[cnt];
56         }
57         cnt++;
58     }
59     int count=0;
60     for(int i=1;i<=10;i++){
61         if(ans[i]==res[i]) count++;
62     }
63     r=count/10.0;
64     cout<<fixed<<setprecision(2)<<r*100<<'%'<<'\n';
65     
66     
67     return 0;
68 }
View Code

运行结果截图:

4f131e8c-5a55-476a-b6a5-9e6a871b31ac065233bc-21a1-4093-88a8-d06521d24c2d

实验总结:

1.C++标准库中有很多非常方便的接口函数,熟练掌握可以减少代码量,节省时间。比如这次实验中的reverse,reverse_copy,tolower,toupper等函数。

2.注意区分cin >>与getline的输入差异,解决包含空格的字符串读取问题。

3.初步了解了 lambda 表达式的语法与场景。

 

posted @ 2025-10-17 20:52  pithia  阅读(4)  评论(0)    收藏  举报