OOP 实验1

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

任务1

  • 源代码
点击查看代码
// 现代C++标准库、算法库体验
// 本例用到以下内容:
// 1. 字符串string, 动态数组容器类vector、迭代器
// 2. 算法库:反转元素次序、旋转元素
// 3. 函数模板、const引用作为形参

#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};
    // 将[v1.begin(), v1.end())区间内元素循环左移1位
    rotate(v1.begin(), v1.begin()+1, v1.end());  
    cout << "v1: "; output(v1);

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

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

    vector<int> v4{v0};
    // 将[v1.begin(), v1.end())区间内元素循环右移2位
    rotate(v4.begin(), v4.end()-2, v4.end()); 
    cout << "v4: "; output(v4);
}
+ 测试截图

t1

  • 回答问题
    Q1:reserve与reserve_copy的区别?

    a1:reserve函数是直接将给出的字符串从起始点到末尾点的内容反转,例如strings1(s0);reverse(s1.begin(), s1.end()); 就是先将s0拷贝给s1(s1=0123456789),直接将s1反转为(s1=9876543210)。
    reserve_copy函数则是直接将原函数copy到目标函数里,原函数保持不变,例如reverse_copy(s0.begin(), s0.end(), s2.begin());就是直接把s0copy给s2同时反转,s0保持不变。

    Q2:rotate算法如何改变元素顺序的?三个参数代表什么?

    a2:rotate(first, middle, last);通过移动一部分元素的位置改变元素顺序。
    三个参数分别为 first:区间起点,middle:变成新起点的那个元素,last:区间终点(不含)
    具体实行步骤:将[first, middle) 的部分移到后面,将[middle, last) 的部分移到前面,并且两段内部相对顺序都不变。其中,middle按照左加右减的格式执行移动方向以及个数。

任务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();
}

// 输出容器对象c中的元素
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
    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); 
    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;
}
  • 测试截图

image

  • 回答问题
    Q1:generate 算法的作用是什么?

    a1:generate(first, last, gen) 用函数对象 gen 的返回值为区间 [first, last) 内的每个元素赋值。代码实际过程中,它用我们预先写好的 generate_random_number() 函数产生的随机数填充整个 vector,不需要再手写循环。

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

    a2:minmax_element 是 C++ 标准库 里的一个算法函数。它的作用很简单:一次性找出一个区间内的最小值和最大值。它相当于是一个“扫描仪”,把容器扫一遍,同时记住“最小的”和“最大的”两个元素的位置。返回的 ans 是一个 pair 对象:ans.first 是指向最小值的迭代器,ans.second 是指向最大值的迭代器,用 ans.first 取出最小值,ans.second 取出最大值。如果使用min_element + max_element 需要两次遍历,效率没有minmax_element更高。

    Q3:查询 generate 第3个参数 用法,与使用自定义函数
    generate_random_number 相比,lambda表达式适用场景是什么?

    a3:效果是一样的。lambda 表达式 是一个匿名函数对象,行为与 generate_random_number 函数完全一致,编译后生成的代码逻辑相同。
    lambda 适用场景:逻辑简单、仅需使用一次的函数;避免单独声明和定义函数,使代码更紧凑;需要捕获上下文变量时(本代码未捕获,但 lambda 支持 [&] 或 [=] 捕获);常用于算法参数(如 generate, sort, transform 的自定义比较/操作)。

任务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';
}
+ 测试截图

t3

  • 回答问题
    Q1:自定义函数 func 功能是什么?

    a1:小写 'z' 变成 'a',大写 'Z' 变成 'A',其他字母字符变成字母表中的下一个字符(如 'a'→'b','B'→'C'),非字母字符保持不变.

    Q2:tolower 和 toupper 功能分别是什么?

    a2:tolower:如果 c 是大写字母,返回对应的小写字母,否则返回 c 本身。toupper:如果 c 是小写字母,返回对应的大写字母,否则返回 c 本身。

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

    a3:四个参数含义:first:输入区间的起始(如 s1.begin());last:输入区间的结束迭代器(如 s1.end());d_first:输出区间的起始,结果会写入从这个位置开始的容器;op:一元操作函数,对输入区间的每个元素调用这个函数,结果存入输出位置。如果第3个参数改成 s1.begin():输出目标变成了 s1 自身,即原地变换。此时 s1 的内容会被 func 处理后的结果覆盖,原始字符串丢失。而原代码使用 s2.begin() 是将变换结果写入新字符串 s2,保留原字符串 s1 不变。

任务4

  • 源代码
点击查看代码
#include<iostream>
#include<string>
#include<algorithm>
#include<cctype>      // 提供 tolower 函数

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 left = 0;                                     //头部 
    int right = static_cast<int>(s.length()) - 1;     //尾部,将s的长度转换为int 
    
    while(left < right){                                    
        if(s[left] != s[right]){                      //比较头部s[left]和尾部s[right],不相等直接不是回文 
            return false;   
        }
        left++;
        right--;                                      //向中间部分检查 
    }
    return true;                                      // 全部对称,是回文
}

// 函数is_palindrome_ignore_case定义:忽略大小写,先统一转小写再比较 
bool is_palindrome_ignore_case(const std::string&s){
    std::string lower_s(s.length(), ' ');             //准备一个字符串lower_s 
    for(size_t i = 0; i < s.length(); i++){
        lower_s[i] = static_cast<char>(std::tolower(static_cast<unsigned char>(s[i])));  //tolower转小写
    }
    // 判断转换后的字符串是否为回文,同上 
    int left = 0;
    int right = static_cast<int>(lower_s.length()) - 1;
    
    while(left < right){
        if(lower_s[left] != lower_s[right]){
            return false;
        }
        left++;
        right--;
    }
    return true;
}
+ 测试截图

t4

  • 回答问题
    Q:使用 cin >> s 输入时,输入的字符串中不能包含空格。如果希望测试字符串包含空格(如 hello
    oop ),代码应如何调整?

    a:使用标准库中getline函数,默认以换行作为终止符号,getline(输入流[如cin],str[对象,如本代码中字符串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"; 
    }
}

std::string dec2n(int x,int n){
	const char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";   //先定义一个digits字符串后面用 
	if(x<0){
		return "false";                     //排除负数 
	}
	if(x==0){
		return "0";
	}
	std::string y;
	int t=x;
	while(t>0){
		int yushu = t % n;
		y += digits[yushu];                 //digits取字符,不用写一堆 if把10-35转化成 a~z
		t /= n;
	}
	std::reverse(y.begin(), y.end());       // 低位在前,reserve进行逆序
    return y;
} 
+ 测试截图

t5

任务6

  • 源代码
点击查看代码
#include <iostream>
#include <string>
#include <cctype>        //提供toupper函数 
#include <iomanip>       // 格式化输出,使用setw() 
void generate_zimubiao() {
    const char xiaoxie[] = "abcdefghijklmnopqrstuvwxyz";    //26个字母组成的字符串 
    int length = 26;

    for (int i = 0; i <= 26; ++i) {                         //一共生成27行 
        // 构造当前行的循环移位字符串
        std::string row;                                    // 用 string 对象 row 来拼接每一行的输出内容
        for (int j = 0; j < length; ++j) {
            char c = xiaoxie[(j + i) % 26];                 //(j当前位置 + i行号) % 26,用取余确定第一个字母是谁,实现"循环左移"效果
            if (i != 0) {
                c = std::toupper(static_cast<char>(c));     //从第1行开始(不含第0行),每个小写字母转成大写 
            }
            row += " ";                                     //row先追加一个空格
            row += c;                                       //row再追加字母
        }
        if (i == 0) {
            std::cout << "  " << row << "\n";              // 第0行:不显示行号,前面用2个空格缩进对齐
        }
        else{
        std::cout << std::setw(2) << i << row << "\n";  //第1~26行:行号右对齐占2位std::setw(2)+字母内容(空格已经在row中拼接)
		}
    }
}

int main() {
    generate_zimubiao();
    return 0;
}
+ 测试截图

t6

任务7

  • 源代码
点击查看代码
#include <iostream>
#include <iomanip>   // 使用格式化输出,保留小数点后两位
#include <cstdlib>   // 使用rand() 和 srand()
#include <ctime>     // 使用time()

using namespace std;

int main() {
    srand(time(0));  //设置随机种子:以当前系统时间作为种子,确保每次运行程序生成的题目不同
    
    int correct_count = 0;             
    int total = 10;          
    
    for (int i = 0; i < total; ++i) {
        
        int op_type = rand() % 4;      // 生成操作符:0代表+,1代表-,2代表*,3代表/
        
        // 生成第一个操作数 num1 (范围 1~10)
        int num1 = rand() % 10 + 1;
        // 生成第二个操作数 num2 (范围 1~10)
        int num2 = rand() % 10 + 1;
        int correct_answer = 0;
        char op_char = ' ';                    //预设运算符 
        
        switch (op_type) {
            case 0:                            // 加法
                op_char = '+';
                correct_answer = num1 + num2;
                break;
            case 1:                            // 减法
                op_char = '-';
                if (num1 < num2) {
                    swap(num1, num2);          // 如果第一个数<第二个数,交换 
                }
                correct_answer = num1 - num2;
                break;
            case 2:                            // 乘法
                op_char = '*';
                correct_answer = num1 * num2;
                break;
            case 3:                            // 除法
                op_char = '/';
                while (num1 % num2 != 0) {     //如果不能整除 
                    num2 = rand() % 10 + 1;    //重新生成num2直到可以整除
                }
                correct_answer = num1 / num2;
                break;
        }
        int user_answer;
        cout << num1 << " " << op_char << " " << num2 << " = ";   //输出运算式,用户输入答案
        cin >> user_answer;
        
        if (user_answer == correct_answer) {
            correct_count++;                               //判断是否计算正确 
        }
    }
    
    double accuracy = (static_cast<double>(correct_count) / total) * 100;   //计算并输出正确率,转换正确率格式为double 

    cout << "正确率: " << fixed << setprecision(2) << accuracy << "%" << endl;       //本段查询ai,fixed 和 setprecision(2) 结合使用,强制保留两位小数输出
    return 0;
}
+ 测试截图

t7-1

t7-2

Conclusion

周五打开任务PDF,第一反应是头大、发怵,觉得任务好多。但花零散时间一点点做下来,不管是翻资料,还是部分参考ai理思路,完成时比刚开始踏实了不少。每个任务都在逼着自己动手敲一敲,现在看来,运用C++语法比之前熟练了一些(可能还不够)。逼着自己使用Markdown编辑器写报告,告别了熟悉的Word式一样的HTML编辑器,笨拙归笨拙,也算跳出舒适区了。排版已尽力调整 :)

posted @ 2026-10-19 21:24  AutumnLR  阅读(3)  评论(0)    收藏  举报