Skip to content

Program

write rule of five

big project structure

ConfigManager.h 里面

#pragma once
#include <memory>
#include <string>
#include <optional>

// C++17 Best Practice: only include what you need

class ConfigManager {
public:
    ConfigManager();
    ~ConfigManager();

    // 禁止拷贝,但允许移动
    ConfigManager(const ConfigManager&) = delete;
    ConfigManager& operator=(const ConfigManager&) = delete;
    ConfigManager(ConfigManager&&) noexcept;
    ConfigManager& operator=(ConfigManager&&) noexcept;

    // 对外接口
    bool load(const std::string& filename);
    std::optional<std::string> get(const std::string& key) const;

private:
    struct Impl;                     // 前置声明
    std::unique_ptr<Impl> pimpl_;    // PIMPL 指针
};

在实现里面 ConfigManager.cpp

#include "ConfigManager.h"
#include <fstream>
#include <map>
#include <iostream>
#include <nlohmann/json.hpp>  // heavy dependency: only in .cpp

using json = nlohmann::json;

// 内部实现细节(不暴露)
struct ConfigManager::Impl {
    std::map<std::string, std::string> data;
};

ConfigManager::ConfigManager() 
    : pimpl_(std::make_unique<Impl>()) {}

ConfigManager::~ConfigManager() = default;

// 移动构造 & 赋值
ConfigManager::ConfigManager(ConfigManager&&) noexcept = default;
ConfigManager& ConfigManager::operator=(ConfigManager&&) noexcept = default;

bool ConfigManager::load(const std::string& filename) {
    std::ifstream in(filename);
    if (!in.is_open()) return false;

    json j;
    in >> j;
    for (auto& [k, v] : j.items()) {
        pimpl_->data[k] = v.get<std::string>();
    }
    return true;
}

std::optional<std::string> ConfigManager::get(const std::string& key) const {
    auto it = pimpl_->data.find(key);
    if (it == pimpl_->data.end())
        return std::nullopt;
    return it->second;
}

工程目录结构

project/
├─ CMakeLists.txt
├─ include/                   # 对外可见的公共头文件(安装/被其他目标使用)
  └─ myproj/
     ├─ core/
       ├─ logger.h
       └─ config_manager.h
     ├─ net/
       ├─ http_client.h
       └─ socket.h
     └─ util/
        └─ string_util.h
├─ src/                       # 实现 (.cpp) + 私有头(仅本目标使用)
  ├─ core/
    ├─ logger.cpp
    ├─ config_manager.cpp
    └─ config_manager_impl.h   # 私有头,不放到 include/
  ├─ net/
    ├─ http_client.cpp
    └─ socket.cpp
  └─ util/
     └─ string_util.cpp
├─ tests/
  ├─ CMakeLists.txt
  └─ core/ net/ util/ ...
├─ examples/
  └─ minimal.cpp
└─ third_party/               # 外部依赖(如 header-only 库)

PIMPL

Pointer to IMPLementation “指向实现的指针”, 它是一种用来隐藏类实现细节的技巧。让用户只看到类的“接口(Interface)”,而看不到内部成员和依赖。

private:
    struct Impl;                       // 前置声明 (incomplete type)
    std::unique_ptr<Impl> pimpl_;      // 指向实现

然后在cpp文件里面

include "config_manager.h"
#include <map>
#include <fstream>
#include <nlohmann/json.hpp>

struct ConfigManager::Impl {
    std::map<std::string, std::string> data;
};

ConfigManager::ConfigManager() : pimpl_(std::make_unique<Impl>()) {}
ConfigManager::~ConfigManager() = default;
ConfigManager::ConfigManager(ConfigManager&&) noexcept = default;
ConfigManager& ConfigManager::operator=(ConfigManager&&) noexcept = default;

工程题

设计一个简单的 Logger

要求:

  • 支持多级日志(INFO / WARN / ERROR)。
  • 可以写入文件。
  • 支持线程安全。

定义一个枚举类型来区分日志级别 LogLevel

Logger: 使用 线程安全懒汉式单例。通过 getInstance() 获取唯一实例。禁止拷贝构造与赋值

在构造函数中打开一个文件流(std::ofstream)。每次写日志时,写入 [时间][级别][内容]。可配置是否同时输出到 std::cout

多线程写日志可能并发访问输出流,必须加锁。

使用 std::chrono 获取当前时间。使用 std::put_time(来自 <iomanip>) 格式化时间

或从配置文件读取参数(结合前面提到的 ConfigParser

实现一个 SmartPointer

  • 模拟 std::shared_ptr 的功能(引用计数)。
  • 支持拷贝和移动语义。
  • 析构时自动释放

实现一个 Timer

要求:

  • 在后台线程中定时执行一个回调函数。
  • 可以启动 / 停止 / 重复定时任务。

设计一个简单的 ConfigParser

要求:

  • 从文本文件(例如 .ini)读取键值对配置。
  • 提供 get<T>(key) 接口,支持类型转换。

实现一个简单的内存池 MemoryPool

要求:

  • 预分配固定大小的内存块。
  • 提供 allocate / deallocate
  • 支持多线程安全可选。

简易 ThreadPool

要求:

  • 支持提交任务(lambda / function)。
  • 自动调度多个 worker 线程执行。
  • 支持优雅关闭。

实现一个 CircularBuffer(环形缓冲区)

要求:

  • 固定容量。
  • 支持多线程生产者/消费者。
  • 提供 push / pop

设计一个资源管理类(如 FileHandle

要求:

  • 打开文件、读写、关闭。
  • 支持异常安全。
  • 不允许资源泄漏。

实现一个轻量事件系统

要求:

  • 注册回调函数(监听器)。
  • 触发事件时调用所有监听器。
  • 支持添加/移除监听。

设计一个简单的性能计时器 ScopedTimer

要求:

  • 构造时记录时间,析构时输出耗时。
  • 可用于代码块性能测试。

题目示例:实现一个简易任务调度器

  • 支持注册任务(函数 + 延迟时间)
  • 支持周期任务
  • 后台线程定时执行
  • 支持线程安全关闭