实验1_实验报告

实验1:现代C++编程初体验

任务1

  • 源代码
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

//模版函数声明
template<typename T >
void output(const T &c);
void test1();
void test2();
void test3();

int main(){
    std::cout << "测试1:\n";
    test1();

    std::cout << "\n测试2:\n";
    test2();

    std::cout <<"\n测试3:\n";
    test3();
}
//输出容器对象c中的元素
template <typename T>
void output (const T &c){
    for (auto &i:c)
        std::cout << i << ' ';
    std::cout << '\n';
}
//测试1:组合使用算法库、迭代器、string反转字符串
void test1(){
    using namespace std;

    string s0{"0123456789"};
    cout << "s0 = "<< s0 <<endl;

    string s1(s0);
    //反转s1自身
    reverse(s1.begin(), s1.end());
    cout << "s1 = " << s1 <<endl;

    string s2(s0.size(),' ');
    //将s0反转后结果拷贝到s2,s0自身不变
    reverse_copy(s0.begin(), s0.end(), s2.begin());
    cout << "s2 = " << s2 << endl; 
}

//测试2:组合使用算法库、迭代器、vector反转动态数组对象vector内数据
void test2(){
    using namespace std;
    
    vector<int> v0{2,0,4,9};
    cout << "v0:"; output(v0);

    vector<int> v1{v0};
    reverse(v1.begin(), v1.end());
    cout << "v1:"; output(v1);

    vector<int>v2{v0};
    reverse_copy(v0.begin(), v0.end(), v2.begin());
    cout << "v2:"; output(v2);
}

//测试3:组合使用算法库、迭代器、vector实现元素旋转移位
void test3(){
    using namespace std;

    vector<int> v0{0,1,2,3,4,5,6,7,8,9};
    cout << "v0:"; output(v0);

    vector<int>v1{v0};
    rotate(v1.begin(), v1.begin()+1, v1.end());//将[v1.begin(),v1.end())区间内元素循环左移1位
    cout << "v1:"; output(v1);

    vector<int>v2{v0};
    rotate(v2.begin(), v2.begin()+2, v2.end());//循环左移2位
    cout << "v2:"; output(v2);

    vector<int>v3{v0};
    //rotate(v3.begin(), v3.begin()-1, v3.end());  越界访问,正确语法应该是v3.end()-1
    rotate(v3.begin(), v3.end()-1, v3.end());//循环右移1位
    cout << "v3:"; output(v3);

    vector<int>v4{v0};
    //rotate(v4.begin(), v4.begin()-2, v4.end());  同v3
    rotate(v4.begin(), v4.end()-2, v4.end());//循环右移2位
    cout << "v4:"; output(v4);
}
  • 运行测试截图

image

  • 问题回答
    • 问题1:reverse 和reverse_copy 有什么区别?
      reverse(first,last)表示原地反转,原容器被修改,reverse_copy(first,last,result)保持源区间不变,把反转后的结果写进目标位置(result)中。同时需要注意reverse_copy不会帮忙申请空间,只负责往 s2.begin() 开始的位置写,所以必须先开好大小刚好的空间,如任务1中用 s2(s0.size(), ' ')创建大小和s0一样的字符串s2。
    • 问题2:rotate 算法是如何改变元素顺序的?它的三个参数分别代表什么?
      rotate(first,middle,last)是原地操作的算法,将middle处的元素旋转成新起点,修改哪个容器由前两个区间的迭代器决定。first 是区间的起点,last 区间的终点(不含),这两个必须来自同一个容器。middle是“分割点”,旋转后它位置的元素变成新起点。要求是first<=middle<=last。middle=begin()+n:左移n位;middle=end()-n:右移n位。

任务2

  • 源代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <iomanip>
#include <cstdlib>
#include <ctime>

template<typename T>
void output(const T&c);

int generate_random_number();
void test1();
void test2();

int main(){
    std::srand(std::time(0));
    std::cout << "测试1:\n";
    test1();
    std::cout << "\n测试2:\n";
    test2();
}

template<typename T>
void output(const T&c){
    for(auto &i:c)
        std::cout << i << ' ';
    std::cout << '\n';
}

// 返回[0,100]区间内的一个随机整数
int generate_random_number(){
    return std::rand() %101;
}

// 测试1:对容器类对象指定迭代器区间赋值、排序
void test1(){
    using namespace std;

    vector<int> v0(10);  // 创建一个动态数组对象v0,对象大小为10
    generate(v0.begin(), v0.end(), generate_random_number);  // 生成随机数填充v0
    //generate(v0.begin(), v0.end(), [](){return std::rand()%101;});
    cout << "v0:"; output(v0);

    vector<int> v1{v0};
    sort(v1.begin(), v1.end());  // 对整个vector排序
    cout << "v1:"; output(v1);

    vector<int> v2{v0};
    sort(v2.begin()+1, v2.end()-1); // 只对中间部分排序,不包含首尾元素
    cout << "v2:"; output(v2);
}

//测试2:对容器类对象指定迭代器区间赋值、计算最大值/最小值/均值
void test2(){
    using namespace std;

    vector<int>v0(10);
    generate(v0.begin(), v0.end(), generate_random_number);
    //generate(v0.begin(), v0.end(), [](){return std::rand()%101;});
    cout << "v0:"; output(v0);

    //求最大值和最小值
    auto min_iter = min_element(v0.begin(), v0.end());
    auto max_iter = max_element(v0.begin(), v0.end());
    cout << "最小值:" << *min_iter << endl;
    cout << "最大值:" << *max_iter << endl;

    //同时求最大值和最小值
    auto ans = minmax_element(v0.begin(), v0.end());
    cout << "最小值:" << *(ans.first) << endl;
    cout << "最大值:" << *(ans.second) << endl;

    //求平均值
    double avg1 = accumulate(v0.begin(), v0.end(), 0.0) / v0.size();
    cout << "均值:" << fixed << setprecision(2) << avg1 << endl;

    sort (v0.begin(), v0.end());
    double avg2 = accumulate(v0.begin()+1, v0.end()-1, 0.0) / (v0.size()-2);
    cout << "去掉最大值、最小值之后,均值:" << avg2 << endl;
}
  • 运行测试截图

屏幕截图 2026-10-22 235336

  • 问题回答
    • 问题1:generate算法的作用是什么?
      generate()算法的作用是给一个区间[first,last)内的每个元素逐个赋值,即[v0.begin(), v0.end())区间,赋的值由generate_random_number决定。

    • 问题2:minmax_element和分别调用min_element、max_element相比,有什么优势?
      minmax_element函数相比分别调用min_element、max_element函数,只遍历一次数据,但一次调用输出两个结果,在数据量大时比较有优势。

    • 问题3:lambda表达式替换掉generate_random_number函数后,效果是否等同,lambda表达式的用法。
      换成lambda表达式后效果相同,执行过程是对v0中从头到尾的所有元素,各调用一次,填入返回值。
      lambda表达式的用法:Lambda 是 C++11 引入的内联的、可捕获外层变量的匿名函数,写法像一段"就地定义、就地使用"的小函数。格式是:[捕获列表](参数列表) mutable -> 返回类型 {函数体}。其中[捕获列表]可以为空[],表示完全不捕获任何外层变量;[x]/[=]表示按值捕获;[&x]/[&]表示按引用捕获。参数列表和普通函数参数一样,可省;mutable表示允许修改按值捕获的变量,可选; -> 类型表示显式指定返回类型,可省(自动推导);{函数体}内含执行代码,必须有。

任务3

  • 源代码
#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';
}
  • 运行测试截图

屏幕截图 2026-10-23 005641

  • 问题回答
    • 问题1:自定义函数func()功能是什么?
      func()功能是使每个英文字母变成字母表中它的下一个字母,'z' 和 'Z' 回绕到 'a' 和 'A',非字母字符原样不动。
    • 问题2:tolower和toupper功能分别是什么?
      tolower:实现大写转小写功能;toupper实现小写转大写功能。
    • 问题3:transform的4个参数意义分别是什么?如果把第3个参数s2.begin()改成s1.begin(),有何区别?
      transform函数是对整个字符串进行操作。第1个和第2个参数表示源区间[第1个参数,第2个参数),第3个参数表示结果写入位置的迭代器,第4个参数表示对源区间每个元素调用此函数,返回值写入第3个参数。如果把第3个参数s2.begin()改成s1.begin(),实现的是“就地转换”功能,转换后的字符串写回原处。

任务4

  • 源代码
#include <iostream>
#include <string>
#include <algorithm>

bool is_palindrome(const std::string &s);
bool is_palindrome_ignore_case(const std::string &s);

int main() {
   using namespace std;
   string s;

    // 多组输入,直到按下Ctrl+Z结束测试
    while(cin >> s) {
        cout << boolalpha 
             << "区分大小写: " << is_palindrome(s) << "\n"
             << "不区分大小写: " << is_palindrome_ignore_case(s) << "\n\n";
    }     
}

// 函数is_palindrome定义
bool is_palindrome(const std::string &s){
    int i = 0, j = (int)s.size() - 1;
    while (i < j) {
        if (s[i] != s[j])
            return false;
        i++; j--;
    }
    return true;
}

// 函数is_palindrome_ignore_case定义
bool is_palindrome_ignore_case(const std::string &s){
    int i = 0, j = (int)s.size() - 1;
    while (i < j) {
        auto a = std::tolower(s[i]);
        auto b = std::tolower(s[j]);
        if (a != b)
            return false;
        i++; j--;
    }
    return true;
}
  • 运行测试截图

屏幕截图 2026-10-23 022241

  • 问题回答
    • 问题:使用cin >> s输入时,输入的字符串中不能包含空格。如果希望测试字符串包含空格(如hello oop),代码应如何调整?
      可以使用getline()函数,读取整行时包含空格,将源代码while(cin >> s)改成while (getline(cin, s))。

任务5

  • 源代码
#include <iostream>
#include <string>
#include <algorithm>

std::string dec2n(int x, int n = 2);

int main() {
    int x;
    while(std::cin >> x) {
        std::cout << "十进制: " << x << '\n'
                  << "二进制: " << dec2n(x) << '\n'
                  << "八进制: " << dec2n(x, 8) << '\n'
                  << "十二进制: " << dec2n(x, 12) << '\n'
                  << "十六进制: " << dec2n(x, 16) << '\n'
                  << "三十二进制: " << dec2n(x, 32) << "\n\n"; 
    }
}

// 函数dec2n定义
std::string dec2n(int x, int n) {
    const std::string digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    std::string result;
    int m;
    do {  
        m = x % n;                               
        result += digits[m];         
        x = x / n;                          
    } while (x > 0);

    std::reverse(result.begin(), result.end());
    return result;
}
  • 运行测试截图

屏幕截图 2026-10-23 025645

任务6

  • 源代码
#include <iostream>
#include <string>
#include <algorithm>
#include <iomanip>
#include <cctype>
using namespace std;

int main(){
    //构造原始字母表
    string s;
    for (char c = 'a'; c <= 'z'; c++)
        s += c;

    //打印表头
    cout << "   ";
    for (char c:s)
        cout << c << ' ';
    cout << '\n';

    //打印26行密文
    for (int i =1; i <= 26; i++){
        rotate(s.begin(), s.begin()+1, s.end());
        
        //打印行号
        cout << setw(2) << i << ' ';
        
        for (char c : s)
            cout << char(toupper(c)) << ' ';
        cout << '\n';
    }
}

  • 运行测试截图

屏幕截图 2026-10-23 034732

任务7

  • 源代码
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <iomanip>
using namespace std;

int main() {
    srand(time(0));        // 随机种子,保证每次运行题目不同

    int score = 0;
    

    for (int i = 0; i < 10; i++) {
        // 1. 生成两个操作数 [1, 10]
        int a = rand() % 11;
        int b = rand() % 11;
        
        // 2. 随机选运算符 0:+, 1:-, 2:*, 3:/
        int op = rand() % 4;
        
        // 3. 按运算符调整操作数,保证题目合法
        if (op == 1 && a < b)              // 减法:保证 a >= b
            swap(a, b);
        if (op == 3) {                     // 除法:保证 a 能被 b 整除
            a = b * (rand() % 101);     // 被除数 = 除数 × 倍数,[1,100]
        }

        // 4. 计算正确答案
        int ans;
        char sym;
        switch (op) {
            case 0: ans = a + b; sym = '+'; break;
            case 1: ans = a - b; sym = '-'; break;
            case 2: ans = a * b; sym = '*'; break;
            case 3: ans = a / b; sym = '/'; break;
        }

        // 5. 出题并读取用户答案
        int user;
        cout << a << ' ' << sym << ' ' << b << " = ";
        cin >> user;

        if (user == ans)
            score++;
    }

    // 6. 输出正确率
    cout << "正确率: " << fixed << setprecision(2) << score * 100.0 / 10 << "%\n";
}
  • 运行测试截图
    image
    image

实验总结

1、任务1认识了reverse(first,last)和reverse_copy(first,last,result)以及rotate(first,middle,last)三种算法,并通过AI的帮助修改了源代码中有关middle的语句,掌握了“middle=begin()+n:左移n位;middle=end()-n:右移n位”的规则。
2、任务2主要学习了lambda表达式的用法,没有lambda的话,就得在函数外面专门定义一个全局函数或仿函数类,还要操心变量怎么传进去,lambda把这一切都写在了使用处,使代码紧凑,逻辑清晰。
3、任务3学习了transform函数,相比于tolower和toupper函数只对一个字符进行转换大小写,transform函数对整个字符串进行操作,实际操作更便捷。
4、任务4主要使用双指针的方法,一头一尾向中间移动进行对比,判断回文串。在[不区分大小写回文]判断中,可以使用字符串转换,把所有字符转换成大写/小写,再进行字符对比。
5、任务5进制转换的核心是短除法:反复用 x % n 取最低位、x / n 缩小,直到商为 0。由于先得到的是最低位,所有余数倒着收集、最后 reverse 翻转回来。
6、任务6结合rotate函数、toupper函数和setw函数输出字母密文对照表,rotate函数用于左移1位,toupper函数将表头小写转换成大写,setw函数使行号对齐。
7、任务7使用rand()函数生成随机数,通过定义op函数实现运算符的定义,将系统计算正确答案和用户计算答案比较,用if语句存储正确次数,再通过公式计算正确率,并按格式输出。

posted on 2026-10-23 04:24  ASa_08  阅读(2)  评论(0)    收藏  举报