C++算法学习-创建新项目工具
C++算法学习
这是一个记录C++算法学习的博客,记录一些算法的代码实现,算法的思路,算法的时间复杂度,算法的空间复杂度,算法的稳定性等等。
该项目使用VScode编写,使用C++编写,使用CMake构建项目,使用Git进行版本控制,使用GitHub进行代码托管。
点击前往CPlusAlgo
目录
创建新项目工具
简介
这是一个用于创建新项目的工具,它可以帮助你快速地创建一个新的项目,并且可以自动生成项目的目录结构,包括源代码文件、头文件、测试文件等等。
使用方法
首先使用cmake生成项目文件
1
2
3cd 000-CreateNewProject
cmake -G "MinGW Makefiles" -B build -S .
cd build && cmake --build .运行生成的
000-CreateNewProject.exe
文件。1
.\build\000-CreateNewProject\000-CreateNewProject.exe
目录结构如下:
1
2
3
4
5000-CreateNewProject
├── build
│ └── 000-CreateNewProject
│ ├── 000-CreateNewProject.exe
└根据提示输入项目名称,例如
000-CreateNewProject
。- ps:项目名称不能包含空格,不能包含特殊字符,不能包含中文。
- ps:项目名称不能包含空格,不能包含特殊字符,不能包含中文。
代码解析
bool nameCheck(const string &name)
函数- 该函数用于检查项目名称是否合法,如果项目名称包含空格、特殊字符或者中文,则返回false,否则返回true。
name
: 项目名称- 该函数的实现如下:
- 判断项目名称是否为空,如果为空,则返回false。
1
2
3
4
5if (name.empty())
{
std::cout << "文件夹名称不能为空!\n";
return false;
}name.empty()
函数用于判断字符串是否为空,如果为空,则返回true,否则返回false。
- 检查非法字符,如果项目名称包含非法字符,则返回false。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17const std::string invalid_chars = "<>:\"/\\|?*";
// 遍历name中的每个字符
for (char c : name)
{
if (invalid_chars.find(c) != std::string::npos)
{
std::cout << "名称包含非法字符: '" << c << "'\n";
return false;
}
// 控制字符检查
if (std::iscntrl(static_cast<unsigned char>(c)))
{
std::cout << "名称包含控制字符!\n";
return false;
}
}const std::string invalid_chars = "<>:\"/\\|?*"
定义了一个字符串,包含了所有非法字符。for (char c : name){}
是一个范围for循环,用于遍历name
中的每个字符c
。invalid_chars.find(c) != std::string::npos
中invalid_chars.find(c)
在非法字符集合中搜索当前字符c
!= std::string::npos
如果找到了,则返回字符在字符串中的位置,否则返回std::string::npos
。std::string::npos
是一个特殊的值,表示字符串中没有找到指定的字符。- ex.
name
包含非法字符*
则输出:名称包含非法字符: '*'
并返回false。
std::iscntrl(static_cast<unsigned char>(c))
用于判断字符c
是否为控制字符,如果是,则返回true,否则返回false。std::iscntrl
函数用于判断字符是否为控制字符,如果是,则返回true,否则返回false。static_cast<unsigned char>(c)
将字符c
转换为无符号字符。static_cast
是C++中的类型转换运算符,用于将一种类型转换为另一种类型。unsigned char
是无符号字符类型,用于表示无符号字符。
- 检查项目名称是否是系统保留名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 检查保留名称 (Windows)
const std::vector<std::string> reserved_names = {
"CON", "PRN", "AUX", "NUL",
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"};
// 转换为大写以检查保留名称
std::string upper_name = name;
std::transform(upper_name.begin(), upper_name.end(), upper_name.begin(), ::toupper);
for (const auto &reserved : reserved_names)
{
if (upper_name == reserved)
{
std::cout << "名称 '" << name << "' 是系统保留名称!\n";
return false;
}
}const std::vector<std::string> reserved_names = {"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"};
定义了一个字符串向量,包含了所有系统保留名称。std::string upper_name = name;
将项目名称转换为小写。std::transform(upper_name.begin(), upper_name.end(), upper_name.begin(), ::toupper);
将项目名称中的每个字符转换为大写。std::transform()
函数用于将一个范围内的元素转换为另一个值。upper_name.begin()
和upper_name.end()
是迭代器,表示upper_name
的开始和结束位置。::toupper
是一个函数对象,用于将字符转换为大写。
for (const auto &reserved : reserved_names){}
是一个范围for循环,用于遍历reserved_names
中的每个字符串reserved
。if (upper_name == reserved){}
判断项目名称是否是系统保留名称,如果是,则返回false。
- 检查项目名称开头和结尾是否
1
2
3
4
5
6
7
8
9
10
11
12// 检查开头和结尾的非法字符
if (name.front() == ' ' || name.front() == '.')
{
std::cout << "名称不能以空格或点开头!\n";
return false;
}
if (name.back() == ' ' || name.back() == '.')
{
std::cout << "名称不能以空格或点结尾!\n";
return false;
}name.front()
和name.back()
分别表示项目名称的第一个字符和最后一个字符。
- 检查名称长度
1
2
3
4
5
6// 检查名称长度
if (name.length() > 255)
{
std::cout << "名称过长(最大255字符)!\n";
return false;
}name.length()
函数用于获取项目名称的长度。
bool createFolder(const fs::path &parent_path, const string &folderName);
函数该函数用于创建文件夹,如果文件夹创建失败返回false,否则返回true。
parent_path
: 父路径folderName
: 文件夹名称该函数的实现如下:
- 拼接父路径和文件夹名称,得到创建路径,如果文件夹已存在,输出提示信息并返回false。
1
2
3
4
5
6
7
8// 拼接父路径和文件夹名称,得到创建路径
fs::path createPath = parent_path / folderName;
// 如果文件夹已存在,输出提示信息并返回false
if (fs::exists(createPath))
{
cout << "文件夹已存在" << endl;
return false;
}fs::path
是C++17中引入的文件系统库中的类,用于表示文件路径。parent_path / folderName
表示将父路径和文件夹名称拼接起来,得到创建路径。fs::exists(createPath)
函数用于判断路径是否存在,如果存在返回true,否则返回false。
- 尝试创建文件夹,如果创建文件夹成功,输出成功信息并返回true,否则输出失败信息并返回false。
1
2
3
4
5
6
7
8
9// 如果创建文件夹成功,输出成功信息并返回true
if (fs::create_directory(createPath))
{
cout << "成功创建文件夹: " << createPath << "\n";
return true;
}
// 如果创建文件夹失败,输出失败信息并返回false
cerr << "无法创建文件夹: " << createPath << "\n";
return false;fs::create_directory(createPath)
函数用于创建文件夹,如果创建成功返回true,否则返回false。cerr
是C++标准库中的错误输出流,用于输出错误信息。
- 捕获文件系统错误,输出错误信息并返回false。
1
2
3
4
5
6// 捕获文件系统错误,输出错误信息并返回false
catch (const fs::filesystem_error &e)
{
cerr << "文件系统错误: " << e.what() << "\n";
return false;
}catch (const fs::filesystem_error &e)
用于捕获文件系统错误,e.what()
函数用于获取错误信息。
optional<fs::path> findAimFolder(const fs::path &start_path, const string &target_name, bool find_all = false, bool stop_at_first = false);
函数该函数用于查找目标文件夹,如果找到目标文件夹,返回目标文件夹的路径,否则返回空值。
start_path
: 查找的起始路径target_name
: 目标文件夹名称find_all
: 是否查找所有目标文件夹,默认为falsestop_at_first
: 是否在找到第一个目标文件夹后停止查找,默认为false该函数的实现如下:
- 先创建存储文件路径的动态数组容器,然后获取当前路径的绝对路径,记录根路径用于终止循环,限制最大向上搜索层数,初始化当前搜索层数。
1
2
3
4
5
6
7
8
9
10
11
12// 存储所有匹配路径的容器
vector<fs::path> matches;
// 获取绝对路径确保路径解析正确
fs::path current = fs::absolute(start_path);
// 记录根路径用于终止循环
const fs::path root = current.root_path();
// 最大向上搜索层数,限制深度
int max_depth = 5;
int current_depth = 0;vector<fs::path> matches;
创建了一个动态数组容器,用于存储所有匹配的路径。vector<元素类型> 向量名
C++ 的动态数组容器,可自动扩容,元素类型
指的是数组中元素的类型,向量名
是数组的名字。
fs::path current = fs::absolute(start_path);
获取当前路径的绝对路径,确保路径解析正确。fs::absolute()
函数用于获取路径的绝对路径。
const fs::path root = current.root_path();
记录根路径用于终止循环。root_path()
函数用于获取路径的根路径。
- 循环向上遍历目录树,检查当前目录名是否匹配目标,如果匹配则将匹配路径添加到结果集,如果设置了找到即停止且不需要所有结果,则返回第一个匹配项,到达根目录时停止遍历,如果无法继续向上,则停止遍历,移动到父目录继续搜索。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36// 循环向上遍历目录树
while (current_depth++ < max_depth && current != root)
{
// 调试输出当前检查的路径 (实际使用时可以移除)
// std::cout << "检查: " << current << std::endl;
// 检查当前目录名是否匹配目标
if (current.filename() == target_name)
{
// 将匹配路径添加到结果集
matches.push_back(current);
// 如果设置了找到即停止且不需要所有结果
if (stop_at_first && !matches.empty())
{
return matches.front(); // 返回第一个匹配项
}
}
// 到达根目录时停止遍历 (防止无限循环)
if (current == root)
{
break;
}
// 获取父目录 (当已经是根目录时,parent_path()返回自身)
fs::path parent = current.parent_path();
// 检查是否无法继续向上 (文件系统保护机制)
if (parent == current)
{
break;
}
// 移动到父目录继续搜索
current = parent;
}while (current_depth++ < max_depth && current != root)
循环向上遍历目录树,直到到达根目录或达到最大搜索层数。if (current.filename() == target_name)
检查当前目录名是否匹配目标。filename()
函数用于获取路径的文件名。matches.push_back(current)
将匹配路径添加到结果集。
if (stop_at_first && !matches.empty())
如果设置了找到即停止且不需要所有结果,则返回第一个匹配项。!matches.empty()
判断结果集是否为空。
if (current == root)
到达根目录时停止遍历。fs::path parent = current.parent_path()
获取父目录。parent_path()
函数用于获取路径的父目录。
if (parent == current)
检查是否无法继续向上。parent == current
判断父目录是否与当前目录相同,如果相同则表示无法继续向上。
current = parent
移动到父目录继续搜索。
- 根据查找结果返回相应的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 处理查找结果
if (matches.empty())
{
// 没有找到匹配项
return std::nullopt;
}
else if (find_all)
{
// 返回所有匹配路径 (实际实现需要修改返回类型)
// 此处简化处理返回最近的一个匹配项
return matches.back(); // 最近的祖先目录
}
else
{
// 默认返回最近(最上层)的匹配项
return matches.back();
}if (matches.empty())
如果没有找到匹配项,则返回空值。std::nullopt
表示空值。
else if (find_all)
如果设置了查找所有目标文件夹,则返回所有匹配路径。matches.back()
返回最近的一个匹配项。
else
默认返回最近(最上层)的匹配项。matches.back()
返回最近的一个匹配项。
void copyFiles(const fs::path &src, const fs::path &dst)
函数- 该函数用于复制文件,将源文件复制到目标文件夹。
src
: 源文件路径dst
: 目标文件夹路径- 该函数的实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119std::vector<std::future<void>> futures; // 存储异步任务的结果
// 原子计数器:用于线程安全的进度跟踪
std::atomic<int> filesCopied = 0; // 已复制的文件数
std::atomic<int> directoriesCopied = 0; // 已复制的目录数
std::atomic<int> tasksCompleted = 0; // 新增:跟踪完成的任务数(含失败)
std::atomic<int> copyErrors = 0; // 新增:错误计数器
std::mutex errorMutex; // 新增:保护错误输出
// 获取开始时间(用于计算速度)
auto startTime = std::chrono::steady_clock::now();
auto showProgress = [&]()
{
const int totalTasks = futures.size();
const int completed = tasksCompleted; // 使用tasksCompleted替代filesCopied+directoriesCopied
auto duration = std::chrono::steady_clock::now() - startTime;
double seconds = std::chrono::duration<double>(duration).count();
double progress = (totalTasks > 0) ? (100.0 * completed / totalTasks) : 0.0;
double speed = (seconds > 0.1) ? (filesCopied / seconds) : 0.0;
std::cout << "\r进度: " << std::fixed << std::setprecision(1)
<< progress << "% | "
<< "文件: " << filesCopied
<< " | 目录: " << directoriesCopied
<< " | 错误: " << copyErrors // 显示错误计数
<< " | 速度: " << std::setprecision(1) << speed << " 文件/秒"
<< " | 任务: " << completed << "/" << totalTasks
<< std::flush;
};
// 确保目标目录存在(加入异常处理)
try
{
fs::create_directories(dst); // 创建目标目录
}
catch (const std::exception &e)
{
std::cerr << "创建目标目录失败: " << dst << " - " << e.what() << std::endl;
return;
}
for (const auto &entry : fs::directory_iterator(src))
{
auto target = dst / entry.path().filename(); // 目标路径
if (fs::is_directory(entry))
{
futures.push_back(std::async(std::launch::async, [=, &directoriesCopied, &tasksCompleted, ©Errors, &errorMutex]
{
try
{
// 尝试创建子目录
fs::create_directories(target);
// 递归复制(可能抛出异常)
fs::copy(entry, target, fs::copy_options::recursive | fs::copy_options::overwrite_existing);
directoriesCopied++;
}
catch (const std::exception &e)
{
std::lock_guard<std::mutex> lock(errorMutex);
std::cerr << "\n目录复制错误: " << entry.path() << " -> " << target
<< " - " << e.what() << std::endl;
copyErrors++;
}
tasksCompleted++; // 无论成功失败都标记任务完成
}));
}
else if (fs::is_regular_file(entry))
{
futures.push_back(std::async(std::launch::async, [=, &filesCopied, &tasksCompleted, ©Errors, &errorMutex]
{
try
{
// 尝试复制文件
fs::copy_file(entry, target, fs::copy_options::overwrite_existing);
filesCopied++;
}
catch (const std::exception &e)
{
std::lock_guard<std::mutex> lock(errorMutex);
std::cerr << "\n文件复制错误: " << entry.path() << " -> " << target
<< " - " << e.what() << std::endl;
copyErrors++;
}
tasksCompleted++; // 无论成功失败都标记任务完成
}));
}
}
std::cout << "开始复制,总任务数: " << futures.size() << "\n";
// 修改循环条件:基于tasksCompleted而非成功计数
while (tasksCompleted < static_cast<int>(futures.size()))
{
showProgress();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
showProgress();
std::cout << "\n复制完成! ";
// 处理异步任务中的异常(防止未捕获异常传播)
for (auto &f : futures)
{
try
{
f.get(); // 获取异步结果(可能重新抛出异常)
}
catch (const std::exception &e)
{
// 此处异常已在任务内处理过,此处仅确保不会终止程序
}
}
auto endTime = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
std::cout << "总耗时: " << duration.count() / 1000.0 << " 秒 | 错误总数: " << copyErrors << "\n";std::vector<std::future<void>> futures;
用于存储异步任务的结果。std::future<void>
表示一个异步操作的状态。
std::atomic<int> filesCopied = 0;
:用于记录已复制的文件数。std::atomic<int> directoriesCopied = 0;
:用于记录已复制的目录数。std::atomic<int> tasksCompleted = 0;
:跟踪已完成的任务数(包含失败的任务数)。- ps.使用atomic(原子操作)可以避免多线程同时修改时的数据竞争。
std::mutex errorMutex;
:用于保护错误计数器,防止多线程同时修改时出现数据竞争。auto startTime = std::chrono::steady_clock::now();
获取开始时间(用于计算速度)。auto showProgress = [&]() {...};
进度显示函数,用于显示复制进度。const int totalTasks = futures.size();
获取总任务数。const int completed = tasksCompleted;
获取已完成任务数。auto duration = std::chrono::steady_clock::now() - startTime;
计算耗时。double seconds = std::chrono::duration<double>(duration).count();
将耗时转换为秒。duration
模板的基本结构:1
2template <class Rep, class Period = std::ratio<1>>
class duration;常用类型 等价表示 说明 std::ratio<1>
1秒 默认单位 std::ratio<1,1000>
1毫秒 千分之一秒 std::ratio<1,1000000>
1微秒 百万分之一秒 std::chrono::duration<double>
类型部分- 这是模板类的类型声明
double
表示内部存储类型为双精度浮点数- 时间单位为秒(默认std::ratio<1>)
- ps.可以通过修改
duration
为milliseconds
来使用毫秒模板。
- ex.
std::chrono::milliseconds
(duration)
变量部分- 这是一个已经存在的duration对象
- 实际要转换的时间间隔值
- 通常通过时间点计算得到
std::chrono::duration<double>(duration)
属于类型转换部分- 创建新的duration对象
- 指定内部存储类型为double(浮点数)
- 自动转换为以秒为单位的持续时间
.count()
提取数值- 返回duration对象内部存储的数值
- 由于指定为double类型,返回值为双精度浮点数
double progress = (totalTasks > 0) ? (100.0 * completed / totalTasks) : 0.0;
当任务数大于0时,计算进度百分比,否则进度为0。double speed = (seconds > 0.1) ? (filesCopied / seconds) : 0.0;
:当耗时超过0.1秒时,计算平均速度,否则速度为0。std::cout << "\r进度: " << std::fixed << std::setprecision(1)
\r
回车符,使光标回到行首,用于覆盖之前的内容。std::fixed
设置浮点数输出为固定小数点格式。std::setprecision(1)
设置输出精度为1位小数。
<< std::flush;
std::flush
强制刷新输出缓冲区,确保输出立即显示,而不是等待缓冲区满或程序结束。
创建目录异常处理
1
2
3
4
5
6
7
8
9try
{
fs::create_directories(dst);
}
catch (const std::exception &e)
{
std::cerr << "创建目标目录失败: " << dst << " - " << e.what() << std::endl;
return;
}fs::create_directories(dst);
创建目标目录catch (const std::exception &e)
捕获异常std::cerr << "创建目标目录失败: " << dst << " - " << e.what() << std::endl;
输出错误信息
for (const auto &entry : fs::directory_iterator(src))
fs::directory_iterator(src)
创建一个迭代器,用于遍历源目录中的所有条目。for (const auto &entry : ...)
遍历每个条目。const auto &entry
使用常量引用,避免不必要的拷贝,同时保证条目不会被修改。
auto target = dst / entry.path().filename();
dst / entry.path().filename()
使用/
运算符将目标目录与条目的文件名连接起来,生成目标路径。entry.path().filename()
获取条目的文件名或目录名。
fs::is_directory(entry)
- 判断条目是否为目录。
fs::create_directories(target);
- 创建目标目录
创建异步任务复制子目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19futures.push_back(std::async(std::launch::async, [=, &directoriesCopied, &tasksCompleted, ©Errors, &errorMutex]
{
try
{
// 尝试创建子目录
fs::create_directories(target);
// 递归复制(可能抛出异常)
fs::copy(entry, target, fs::copy_options::recursive | fs::copy_options::overwrite_existing);
directoriesCopied++;
}
catch (const std::exception &e)
{
std::lock_guard<std::mutex> lock(errorMutex);
std::cerr << "\n目录复制错误: " << entry.path() << " -> " << target
<< " - " << e.what() << std::endl;
copyErrors++;
}
tasksCompleted++; // 无论成功失败都标记任务完成
}));futures.push_back(...);
将异步任务的结果存储在futures
向量中,以便稍后处理。std::async(std::launch::async, [=, &directoriesCopied] {...});
创建一个异步任务std::async
: 异步任务创建函数std::launch::async
: 启动策略,表示任务将在另一个线程中异步执行。[=, &directoriesCopied]
: 捕获列表,按值捕获所有变量,按引用捕获directoriesCopied
。{...}
: 异步任务的内容,使用lambda表达式定义。
try {...} catch (const std::exception &e) {...}
异常处理try {...}
尝试创建子目录并复制整个子目录,递归复制,覆盖已存在的文件。catch (const std::exception &e) {...}
捕获异常std::lock_guard<std::mutex> lock(errorMutex);
创建互斥锁,保证在多线程环境下安全地访问共享资源。std::cerr << "\n目录复制错误: " << entry.path() << " -> " << target << " - " << e.what() << std::endl;
输出错误信息
fs::copy(entry, target, fs::copy_options::recursive | fs::copy_options::overwrite_existing);
复制整个子目录,递归复制,覆盖已存在的文件。fs::copy
: 复制函数entry
: 源路径target
: 目标路径fs::copy_options::recursive
: 递归复制子目录fs::copy_options::overwrite_existing
: 覆盖已存在的文件
directoriesCopied++;
原子递增目录计数器。
else if (fs::is_regular_file(entry))
-判断条目是否为普通文件。
创建异步任务复制文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16futures.push_back(std::async(std::launch::async, [=, &filesCopied, &tasksCompleted, ©Errors, &errorMutex]
{
try
{
fs::copy_file(entry, target, fs::copy_options::overwrite_existing);
filesCopied++;
}
catch (const std::exception &e)
{
std::lock_guard<std::mutex> lock(errorMutex);
std::cerr << "\n文件复制错误: " << entry.path() << " -> " << target
<< " - " << e.what() << std::endl;
copyErrors++;
}
tasksCompleted++; // 无论成功失败都标记任务完成
}));futures.push_back(...);
将异步任务的结果存储在futures
向量中,以便稍后处理。std::async(std::launch::async, [=, &filesCopied] {...});
创建一个异步任务std::launch::async
: 启动策略,表示任务将在另一个线程中异步执行。[=, &filesCopied]
: 捕获列表,按值捕获所有变量,按引用捕获filesCopied
。{...}
: 异步任务的内容,使用lambda表达式定义。
try {...} catch (const std::exception &e) {...}
异常处理try {...}
尝试复制文件,覆盖已存在的文件。catch (const std::exception &e) {...}
捕获异常std::lock_guard<std::mutex> lock(errorMutex);
创建互斥锁,保证在多线程环境下安全地访问共享资源。
fs::copy_file(entry, target, fs::copy_options::overwrite_existing);
复制单个文件,覆盖已存在的文件。fs::copy_file()
: 复制文件函数entry
: 源路径target
: 目标路径fs::copy_options::overwrite_existing
: 覆盖已存在的文件
filesCopied++;
原子递增文件计数器。
std::cout << "开始复制,总任务数: " << futures.size() << "\n";
futures.size()
: 异步任务的数量
等待异步任务完成并且更新进度
1
2
3
4
5
6// 等待所有异步任务完成,并显示进度
while (filesCopied + directoriesCopied < static_cast<int>(futures.size()))
{
showProgress();
std::this_thread::sleep_for(std::chrono::milliseconds(200)); // 每200ms更新一次
}while (filesCopied + directoriesCopied < static_cast<int>(futures.size()))
当文件和目录复制完成数量小于异步任务数量时,继续循环。showProgress();
更新进度std::this_thread::sleep_for(std::chrono::milliseconds(200));
每隔200毫秒更新一次进度。
等待所有任务完成
1
2
3
4
5
6
7
8
9
10
11for (auto &f : futures)
{
try
{
f.get(); // 获取异步结果(可能重新抛出异常)
}
catch (const std::exception &e)
{
// 此处异常已在任务内处理过,此处仅确保不会终止程序
}
}for (auto &f : futures)
遍历所有异步任务futures
: 异步任务向量
f.get();
获取异步任务的结果,可能重新抛出异常。catch (const std::exception &e)
捕获异常catch (const std::exception &e) {...}
捕获异常
auto endTime = std::chrono::steady_clock::now();
std::chrono::steady_clock::now()
: 获取当前时间点。
auto duration = std::chrono::duration_cast<std::chrono::seconds>(endTime - startTime);
std::chrono::duration_cast<std::chrono::seconds>(endTime - startTime)
: 计算时间间隔,将时间间隔转换为秒。chrono::duration_cast<>
: 时间间隔转换函数std::chrono::seconds
: 时间间隔类型,表示秒。endTime - startTime
: 时间间隔,表示从开始时间到结束时间的间隔。
void createFile(const fs::path &path, const std::string &content)
函数- 该函数用于创建一个文件,并将指定的内容写入该文件。
const fs::path &path
: 源路径const std::string &content
: 文件内容- 该函数实现如下:
1
2
3
4
5
6
7ofstream file(path);
if (!file)
{
cerr << "Error: 无法在 " << path << "下创建文件" << endl;
return;
}
file << content; ofstream file(path)
: 创建一个输出文件流,用于写入文件。使用std::ofstream
在离开作用域时自动调用析构函数关闭文件。
void createCMakeLists(const fs::path &parent_path, const string &projectName)
函数该函数用于在指定的父目录下创建一个CMakeLists.txt文件,并写入指定的项目名称。
const fs::path &parent_path
: 父目录路径const string &projectName
: 项目名称该函数实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21const string cmakeTemplate =
R"(project()" + projectName + R"()
# 添加包含目录
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/include
)
# 收集所有源文件
file(GLOB SOURCES "src/*.cpp")
# 创建可执行文件
add_executable(${PROJECT_NAME} ${SOURCES})
# 设置目标属性
set_target_properties(${PROJECT_NAME} PROPERTIES
OUTPUT_NAME "${PROJECT_NAME}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}"
)
)";
createFile(parent_path / "CMakeLists.txt", cmakeTemplate);const string cmakeTemplate
: CMakeLists.txt文件内容模板R"(project()" + projectName + R"())
: 使用原始字符串字面值,将项目名称插入到模板中。createFile(parent_path / "CMakeLists.txt", cmakeTemplate)
: 调用createFile()
函数,在父目录下创建CMakeLists.txt文件,并将模板内容写入该文件。
void createReadme(const fs::path &parent_path, const string &projectName)
函数- 该函数用于在指定的父目录下创建一个README.md文件,并写入指定的项目名称。
const fs::path &parent_path
: 父目录路径const string &projectName
: 项目名称- 该函数实现如下:
1
2
3
4
5
6
7
8
9
10
11const std::string content =
R"(# )" + projectName + R"(
## 简介
## 需求
## 练习
)";
createFile(parent_path / "readme.md", content); const std::string content
: README.md文件内容R"(# )" + projectName + R"(...)
: 使用原始字符串字面值,将项目名称插入到模板中。createFile(parent_path / "readme.md", content)
: 调用createFile()
函数,在父目录下创建README.md文件,并将模板内容写入该文件。