实验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);
}
- 运行测试截图

- 问题回答
- 问题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位。
- 问题1:reverse 和reverse_copy 有什么区别?
任务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;
}
- 运行测试截图

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

- 问题回答
- 问题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(),实现的是“就地转换”功能,转换后的字符串写回原处。
- 问题1:自定义函数
任务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;
}
- 运行测试截图

- 问题回答
- 问题:使用
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;
}
- 运行测试截图

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

任务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语句存储正确次数,再通过公式计算正确率,并按格式输出。


浙公网安备 33010602011771号