Skip to content

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_server

HTTP服务器实现

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++的实际应用和最佳实践。

本站内容仅供学习和研究使用。