C++ 实战项目
概述
通过实际项目学习C++是最有效的方式。本章通过几个完整的实战项目,展示如何将C++知识综合运用到实际开发中,包括项目架构设计、代码组织、测试等开发流程。
🔧 项目1:文件管理工具
项目架构
cpp
// file_manager.h
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <functional>
namespace file_manager {
class FileInfo {
public:
FileInfo(std::string path, size_t size, std::time_t modified_time);
const std::string& getPath() const { return path_; }
size_t getSize() const { return size_; }
std::time_t getModifiedTime() const { return modified_time_; }
std::string getExtension() const;
bool isDirectory() const;
private:
std::string path_;
size_t size_;
std::time_t modified_time_;
};
class FileFilter {
public:
virtual ~FileFilter() = default;
virtual bool matches(const FileInfo& file) const = 0;
};
class ExtensionFilter : public FileFilter {
public:
explicit ExtensionFilter(std::vector<std::string> extensions);
bool matches(const FileInfo& file) const override;
private:
std::vector<std::string> extensions_;
};
class FileSearcher {
public:
using ProgressCallback = std::function<void(const std::string&, size_t, size_t)>;
explicit FileSearcher(std::unique_ptr<FileFilter> filter = nullptr);
std::vector<FileInfo> search(const std::string& root_path,
bool recursive = true,
ProgressCallback callback = nullptr);
void setFilter(std::unique_ptr<FileFilter> filter);
private:
std::unique_ptr<FileFilter> filter_;
};
class FileOperations {
public:
struct CopyResult {
bool success;
std::string error_message;
size_t bytes_copied;
};
static CopyResult copyFile(const std::string& source, const std::string& destination);
static bool deleteFile(const std::string& path);
static bool moveFile(const std::string& source, const std::string& destination);
static std::vector<std::string> listDirectory(const std::string& path);
};
} // namespace file_manager核心实现
cpp
// file_manager.cpp
#include "file_manager.h"
#include <filesystem>
#include <algorithm>
#include <iostream>
namespace file_manager {
namespace fs = std::filesystem;
FileInfo::FileInfo(std::string path, size_t size, std::time_t modified_time)
: path_(std::move(path)), size_(size), modified_time_(modified_time) {}
std::string FileInfo::getExtension() const {
auto pos = path_.find_last_of('.');
return (pos != std::string::npos) ? path_.substr(pos + 1) : "";
}
bool FileInfo::isDirectory() const {
return fs::is_directory(path_);
}
ExtensionFilter::ExtensionFilter(std::vector<std::string> extensions)
: extensions_(std::move(extensions)) {
for (auto& ext : extensions_) {
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
}
}
bool ExtensionFilter::matches(const FileInfo& file) const {
if (file.isDirectory()) return true;
std::string ext = file.getExtension();
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
return std::find(extensions_.begin(), extensions_.end(), ext) != extensions_.end();
}
FileSearcher::FileSearcher(std::unique_ptr<FileFilter> filter)
: filter_(std::move(filter)) {}
std::vector<FileInfo> FileSearcher::search(const std::string& root_path,
bool recursive,
ProgressCallback callback) {
std::vector<FileInfo> results;
if (!fs::exists(root_path) || !fs::is_directory(root_path)) {
return results;
}
try {
size_t processed = 0;
auto iterator = recursive ?
fs::recursive_directory_iterator(root_path) :
fs::directory_iterator(root_path);
for (const auto& entry : iterator) {
auto file_info = FileInfo(
entry.path().string(),
entry.is_regular_file() ? fs::file_size(entry) : 0,
std::chrono::duration_cast<std::chrono::seconds>(
fs::last_write_time(entry).time_since_epoch()).count()
);
if (!filter_ || filter_->matches(file_info)) {
results.push_back(std::move(file_info));
}
if (callback) {
callback(entry.path().string(), ++processed, 0);
}
}
} catch (const fs::filesystem_error& e) {
std::cerr << "Filesystem error: " << e.what() << std::endl;
}
return results;
}
FileOperations::CopyResult FileOperations::copyFile(const std::string& source,
const std::string& destination) {
try {
auto bytes_copied = fs::file_size(source);
fs::copy_file(source, destination, fs::copy_options::overwrite_existing);
return {true, "", bytes_copied};
} catch (const fs::filesystem_error& e) {
return {false, e.what(), 0};
}
}
bool FileOperations::deleteFile(const std::string& path) {
try {
return fs::remove(path);
} catch (const fs::filesystem_error&) {
return false;
}
}
std::vector<std::string> FileOperations::listDirectory(const std::string& path) {
std::vector<std::string> files;
try {
for (const auto& entry : fs::directory_iterator(path)) {
files.push_back(entry.path().filename().string());
}
} catch (const fs::filesystem_error&) {
// 忽略错误
}
return files;
}
} // namespace file_manager
// 使用示例
int main() {
using namespace file_manager;
// 创建扩展名过滤器
auto filter = std::make_unique<ExtensionFilter>(std::vector<std::string>{"cpp", "h", "hpp"});
// 创建搜索器
FileSearcher searcher(std::move(filter));
// 搜索文件
auto results = searcher.search("/path/to/search", true,
[](const std::string& file, size_t processed, size_t total) {
std::cout << "Processing: " << file << std::endl;
});
std::cout << "Found " << results.size() << " files" << std::endl;
// 显示结果
for (const auto& file : results) {
std::cout << file.getPath() << " (" << file.getSize() << " bytes)" << std::endl;
}
return 0;
}🌐 项目2:简单HTTP服务器
HTTP服务器设计
cpp
// http_server.h
#pragma once
#include <string>
#include <unordered_map>
#include <functional>
#include <vector>
namespace http_server {
struct HttpRequest {
std::string method;
std::string path;
std::unordered_map<std::string, std::string> headers;
std::string body;
std::unordered_map<std::string, std::string> query_params;
};
struct HttpResponse {
int status_code = 200;
std::string status_message = "OK";
std::unordered_map<std::string, std::string> headers;
std::string body;
void setHeader(const std::string& key, const std::string& value) {
headers[key] = value;
}
void setJsonResponse(const std::string& json) {
setHeader("Content-Type", "application/json");
body = json;
}
};
using RequestHandler = std::function<void(const HttpRequest&, HttpResponse&)>;
class HttpServer {
public:
explicit HttpServer(int port);
~HttpServer();
void addRoute(const std::string& method, const std::string& path, RequestHandler handler);
void start();
void stop();
private:
int port_;
int server_socket_;
bool running_;
struct Route {
std::string method;
std::string path;
RequestHandler handler;
};
std::vector<Route> routes_;
void acceptConnections();
void handleClient(int client_socket);
HttpRequest parseRequest(const std::string& raw_request);
std::string generateResponse(const HttpResponse& response);
bool routeRequest(const HttpRequest& request, HttpResponse& response);
};
} // namespace http_serverHTTP服务器实现
cpp
// http_server.cpp
#include "http_server.h"
#include <iostream>
#include <sstream>
#include <thread>
#ifdef _WIN32
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#endif
namespace http_server {
HttpServer::HttpServer(int port) : port_(port), server_socket_(-1), running_(false) {
#ifdef _WIN32
WSADATA wsa_data;
WSAStartup(MAKEWORD(2, 2), &wsa_data);
#endif
}
HttpServer::~HttpServer() {
stop();
#ifdef _WIN32
WSACleanup();
#endif
}
void HttpServer::addRoute(const std::string& method, const std::string& path, RequestHandler handler) {
routes_.push_back({method, path, std::move(handler)});
}
void HttpServer::start() {
server_socket_ = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket_ < 0) {
throw std::runtime_error("Failed to create socket");
}
sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port_);
if (bind(server_socket_, (sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
throw std::runtime_error("Failed to bind socket");
}
if (listen(server_socket_, 10) < 0) {
throw std::runtime_error("Failed to listen on socket");
}
running_ = true;
std::cout << "HTTP服务器启动在端口 " << port_ << std::endl;
acceptConnections();
}
void HttpServer::stop() {
running_ = false;
if (server_socket_ >= 0) {
#ifdef _WIN32
closesocket(server_socket_);
#else
close(server_socket_);
#endif
server_socket_ = -1;
}
}
void HttpServer::acceptConnections() {
while (running_) {
sockaddr_in client_addr{};
socklen_t client_len = sizeof(client_addr);
int client_socket = accept(server_socket_, (sockaddr*)&client_addr, &client_len);
if (client_socket >= 0) {
std::thread([this, client_socket]() {
handleClient(client_socket);
}).detach();
}
}
}
void HttpServer::handleClient(int client_socket) {
char buffer[4096];
int bytes_received = recv(client_socket, buffer, sizeof(buffer) - 1, 0);
if (bytes_received > 0) {
buffer[bytes_received] = '\0';
try {
HttpRequest request = parseRequest(buffer);
HttpResponse response;
if (!routeRequest(request, response)) {
response.status_code = 404;
response.status_message = "Not Found";
response.body = "<h1>404 Not Found</h1>";
}
std::string response_str = generateResponse(response);
send(client_socket, response_str.c_str(), response_str.length(), 0);
} catch (const std::exception& e) {
std::string error_response = "HTTP/1.1 500 Internal Server Error\r\n\r\n";
send(client_socket, error_response.c_str(), error_response.length(), 0);
}
}
#ifdef _WIN32
closesocket(client_socket);
#else
close(client_socket);
#endif
}
HttpRequest HttpServer::parseRequest(const std::string& raw_request) {
HttpRequest request;
std::istringstream stream(raw_request);
std::string line;
// 解析请求行
if (std::getline(stream, line)) {
std::istringstream request_line(line);
std::string version;
request_line >> request.method >> request.path >> version;
}
// 解析头部
while (std::getline(stream, line) && line != "\r" && !line.empty()) {
size_t colon_pos = line.find(':');
if (colon_pos != std::string::npos) {
std::string key = line.substr(0, colon_pos);
std::string value = line.substr(colon_pos + 1);
value.erase(0, value.find_first_not_of(" \t"));
value.erase(value.find_last_not_of(" \t\r") + 1);
request.headers[key] = value;
}
}
return request;
}
std::string HttpServer::generateResponse(const HttpResponse& response) {
std::ostringstream stream;
stream << "HTTP/1.1 " << response.status_code << " " << response.status_message << "\r\n";
stream << "Content-Length: " << response.body.length() << "\r\n";
for (const auto& [key, value] : response.headers) {
stream << key << ": " << value << "\r\n";
}
stream << "\r\n" << response.body;
return stream.str();
}
bool HttpServer::routeRequest(const HttpRequest& request, HttpResponse& response) {
for (const auto& route : routes_) {
if (route.method == request.method && route.path == request.path) {
route.handler(request, response);
return true;
}
}
return false;
}
} // namespace http_server
// 使用示例
int main() {
using namespace http_server;
HttpServer server(8080);
// 添加路由
server.addRoute("GET", "/", [](const HttpRequest& req, HttpResponse& res) {
res.body = "<h1>Welcome to Simple HTTP Server!</h1>";
res.setHeader("Content-Type", "text/html");
});
server.addRoute("GET", "/api/hello", [](const HttpRequest& req, HttpResponse& res) {
res.setJsonResponse(R"({"message": "Hello, World!"})");
});
server.addRoute("GET", "/api/time", [](const HttpRequest& req, HttpResponse& res) {
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
res.setJsonResponse(R"({"current_time": ")" + std::ctime(&time_t) + "\"}");
});
try {
server.start();
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}🎮 项目3:任务管理器
任务管理器设计
cpp
// task_manager.h
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <chrono>
#include <functional>
namespace task_manager {
enum class TaskStatus {
Pending,
InProgress,
Completed,
Cancelled
};
enum class TaskPriority {
Low,
Normal,
High,
Critical
};
class Task {
public:
Task(std::string title, std::string description, TaskPriority priority = TaskPriority::Normal);
// Getters
int getId() const { return id_; }
const std::string& getTitle() const { return title_; }
const std::string& getDescription() const { return description_; }
TaskStatus getStatus() const { return status_; }
TaskPriority getPriority() const { return priority_; }
std::chrono::system_clock::time_point getCreatedTime() const { return created_time_; }
std::chrono::system_clock::time_point getDueTime() const { return due_time_; }
// Setters
void setTitle(const std::string& title) { title_ = title; }
void setDescription(const std::string& description) { description_ = description; }
void setStatus(TaskStatus status) { status_ = status; }
void setPriority(TaskPriority priority) { priority_ = priority; }
void setDueTime(std::chrono::system_clock::time_point due_time) { due_time_ = due_time; }
// 状态操作
void start() { status_ = TaskStatus::InProgress; }
void complete() { status_ = TaskStatus::Completed; }
void cancel() { status_ = TaskStatus::Cancelled; }
// 工具方法
bool isOverdue() const;
std::string toString() const;
private:
static int next_id_;
int id_;
std::string title_;
std::string description_;
TaskStatus status_;
TaskPriority priority_;
std::chrono::system_clock::time_point created_time_;
std::chrono::system_clock::time_point due_time_;
};
class TaskManager {
public:
using TaskPtr = std::shared_ptr<Task>;
using TaskFilter = std::function<bool(const TaskPtr&)>;
// 任务操作
TaskPtr createTask(const std::string& title, const std::string& description,
TaskPriority priority = TaskPriority::Normal);
bool deleteTask(int task_id);
TaskPtr getTask(int task_id);
// 查询和过滤
std::vector<TaskPtr> getAllTasks() const;
std::vector<TaskPtr> getTasksByStatus(TaskStatus status) const;
std::vector<TaskPtr> getTasksByPriority(TaskPriority priority) const;
std::vector<TaskPtr> getOverdueTasks() const;
std::vector<TaskPtr> filterTasks(TaskFilter filter) const;
// 统计
size_t getTaskCount() const { return tasks_.size(); }
size_t getTaskCountByStatus(TaskStatus status) const;
// 批量操作
void completeAllTasks();
void deleteCompletedTasks();
// 序列化
bool saveToFile(const std::string& filename) const;
bool loadFromFile(const std::string& filename);
private:
std::vector<TaskPtr> tasks_;
std::vector<TaskPtr>::iterator findTask(int task_id);
};
} // namespace task_manager任务管理器实现
cpp
// task_manager.cpp
#include "task_manager.h"
#include <algorithm>
#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
namespace task_manager {
int Task::next_id_ = 1;
Task::Task(std::string title, std::string description, TaskPriority priority)
: id_(next_id_++), title_(std::move(title)), description_(std::move(description)),
status_(TaskStatus::Pending), priority_(priority),
created_time_(std::chrono::system_clock::now()),
due_time_(std::chrono::system_clock::now() + std::chrono::hours(24)) {}
bool Task::isOverdue() const {
return std::chrono::system_clock::now() > due_time_ && status_ != TaskStatus::Completed;
}
std::string Task::toString() const {
std::ostringstream oss;
oss << "Task " << id_ << ": " << title_ << "\n";
oss << " Description: " << description_ << "\n";
oss << " Status: ";
switch (status_) {
case TaskStatus::Pending: oss << "Pending"; break;
case TaskStatus::InProgress: oss << "In Progress"; break;
case TaskStatus::Completed: oss << "Completed"; break;
case TaskStatus::Cancelled: oss << "Cancelled"; break;
}
oss << "\n Priority: ";
switch (priority_) {
case TaskPriority::Low: oss << "Low"; break;
case TaskPriority::Normal: oss << "Normal"; break;
case TaskPriority::High: oss << "High"; break;
case TaskPriority::Critical: oss << "Critical"; break;
}
return oss.str();
}
TaskManager::TaskPtr TaskManager::createTask(const std::string& title,
const std::string& description,
TaskPriority priority) {
auto task = std::make_shared<Task>(title, description, priority);
tasks_.push_back(task);
return task;
}
bool TaskManager::deleteTask(int task_id) {
auto it = findTask(task_id);
if (it != tasks_.end()) {
tasks_.erase(it);
return true;
}
return false;
}
TaskManager::TaskPtr TaskManager::getTask(int task_id) {
auto it = findTask(task_id);
return (it != tasks_.end()) ? *it : nullptr;
}
std::vector<TaskManager::TaskPtr> TaskManager::getAllTasks() const {
return tasks_;
}
std::vector<TaskManager::TaskPtr> TaskManager::getTasksByStatus(TaskStatus status) const {
return filterTasks([status](const TaskPtr& task) {
return task->getStatus() == status;
});
}
std::vector<TaskManager::TaskPtr> TaskManager::getOverdueTasks() const {
return filterTasks([](const TaskPtr& task) {
return task->isOverdue();
});
}
std::vector<TaskManager::TaskPtr> TaskManager::filterTasks(TaskFilter filter) const {
std::vector<TaskPtr> result;
std::copy_if(tasks_.begin(), tasks_.end(), std::back_inserter(result), filter);
return result;
}
size_t TaskManager::getTaskCountByStatus(TaskStatus status) const {
return std::count_if(tasks_.begin(), tasks_.end(),
[status](const TaskPtr& task) {
return task->getStatus() == status;
});
}
void TaskManager::completeAllTasks() {
for (auto& task : tasks_) {
if (task->getStatus() != TaskStatus::Cancelled) {
task->complete();
}
}
}
void TaskManager::deleteCompletedTasks() {
tasks_.erase(
std::remove_if(tasks_.begin(), tasks_.end(),
[](const TaskPtr& task) {
return task->getStatus() == TaskStatus::Completed;
}),
tasks_.end());
}
std::vector<TaskManager::TaskPtr>::iterator TaskManager::findTask(int task_id) {
return std::find_if(tasks_.begin(), tasks_.end(),
[task_id](const TaskPtr& task) {
return task->getId() == task_id;
});
}
} // namespace task_manager
// 使用示例
int main() {
using namespace task_manager;
TaskManager manager;
// 创建任务
auto task1 = manager.createTask("学习C++", "完成C++教程学习", TaskPriority::High);
auto task2 = manager.createTask("写代码", "实现文件管理工具", TaskPriority::Normal);
auto task3 = manager.createTask("测试", "编写单元测试", TaskPriority::Low);
// 更新任务状态
task1->start();
task2->complete();
// 查询任务
std::cout << "所有任务:" << std::endl;
for (const auto& task : manager.getAllTasks()) {
std::cout << task->toString() << std::endl << std::endl;
}
std::cout << "进行中的任务数量:" << manager.getTaskCountByStatus(TaskStatus::InProgress) << std::endl;
std::cout << "已完成的任务数量:" << manager.getTaskCountByStatus(TaskStatus::Completed) << std::endl;
return 0;
}总结
项目开发要点
- 架构设计: 清晰的类层次和职责分离
- 接口设计: 易于使用和扩展的API
- 错误处理: 健壮的异常处理机制
- 内存管理: 智能指针和RAII原则
- 代码组织: 合理的文件结构和命名空间
最佳实践
- 单一职责: 每个类专注一个功能
- 依赖注入: 便于测试和扩展
- 接口编程: 面向接口而非实现
- 资源管理: 自动化资源清理
- 异常安全: 保证程序健壮性
技术特性应用
- 现代C++: auto、lambda、智能指针
- STL算法: 高效的数据处理
- 文件系统: C++17文件系统库
- 网络编程: Socket API应用
- 多线程: 并发处理提升性能
扩展建议
- 单元测试: 使用Google Test
- 日志系统: 集成spdlog
- 配置管理: JSON/YAML配置
- 数据库: SQLite集成
- GUI界面: Qt或其他GUI框架
实战项目是检验C++学习效果的最佳方式,通过完整项目的开发,能够深入理解C++的实际应用和最佳实践。