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);
}

-
回答问题
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;
}
- 测试截图

-
回答问题
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';
}

-
回答问题
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;
}

-
回答问题
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;
}

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

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


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

浙公网安备 33010602011771号