基于C++实现内存缓存器的具体方案

 更新时间:2026年11月05日 09:29:24   作者:李迟  
想知道如何为C++服务引入Redis,本文介绍一款基于C++11标准库实现的轻量级内存缓存器SimpleCache,零外部依赖、线程安全、支持惰性过期和后台清理,set/get/exists接口简洁易用,附完整源码和测试用例,助你快速集成到现有工程,需要的朋友可以参考下

本文介绍一个基于 C++11 标准库实现的轻量级内存缓存的工具库。该库覆盖了覆盖常规缓存操作,如set、get等。

1. 背景

某C++的服务程序对外提供幂等接口,除数据库外,没有依赖第三方中间件。近期进行功能迭代,需要使用缓存,常见的解决方案是引入 Redis 等外部缓存中间件。

但我不想使用。一方面,这个程序已经使用了json库、cgi库和oci库,部署比较麻烦,在我接手前,生产环境的若干台已经能正常运行了,我接手后,就持续要部署到不同的环境,鉴于个人的水平问题,没有能力将编码和运维的工作分开,为方便日后的自己,花了一些力气做了容器化改造。

因此,作为集多种不同工程于一身且兼顾研发和运维的我而言,额外的东西能不加就不加,引入额外依赖只是增加烦恼耳。

闲话不提,回到题目本身,本方即设计一款零外部依赖、开箱即用、线程安全的内存缓存器,来满足以下需求:

  • 常规化设计,符合一般人对cache的认知。
  • 可使用多种字段为 key 标记,缓存已处理的请求。
  • 缓存项在可配置的时间(默认 10 分钟)后自动过期。
  • 多线程环境下安全读写。
  • 代码简洁,易于集成到现有 C++ 工程。

2. 设计

2.1 整体架构

┌─────────────────────────────────────────┐
│              SimpleCache                │
│   (核心缓存器,通用 key-value 缓存)        │
├────────────┬────────────┬───────────────┤
│ unordered  │   mutex    │  后台清理线程   │
│   _map     │  (读写锁)   │  (可选)        │
└────────────┴────────────┴───────────────┘

SimpleCache作为通用的 key-value 内存缓存类,提供 set/get/exists/remove/cleanup等接口。

2.2 核心数据结构

struct CacheEntry {
    std::string value;
    std::chrono::steady_clock::time_point expireAt;
};

std::unordered_map<std::string, CacheEntry> m_cache;
std::mutex m_mutex;
  • unordered_map 提供 O(1) 平均查找时间
  • CacheEntry 存储值和过期时间点
  • mutex 保证线程安全,所有公开接口使用 lock_guard 保护

2.3 过期策略:惰性 + 定期双机制

机制触发时机特点
惰性过期每次 exists / get 查询时判断即时清理,不占用额外线程
后台定期清理独立线程按间隔运行 cleanup()防止长期未访问的过期条目堆积

构造函数中 cleanupInterval 参数控制后台清理行为:

explicit SimpleCache(int expireSeconds = 600, int cleanupInterval = 60);
  • cleanupInterval = 0:仅惰性过期,不创建后台线程
  • cleanupInterval > 0:启动后台线程,按指定秒数间隔全量扫描清理

2.4 线程安全设计

  • 所有公开方法首行获取 std::lock_guard<std::mutex>,函数退出时自动释放
  • 后台清理线程与业务线程共享同一 mutex,互斥访问 m_cache
  • 析构时通过 atomic<bool> m_running 通知后台线程退出并 join,确保安全释放

3. 实践

3.1 基本用法

#include "SimpleCache.h"

// 创建缓存器,10 分钟过期,60 秒后台清理
SimpleCache cache(600, 60);

// 写入
cache.set("key1", "value1");

// 查询是否存在
if (cache.exists("key1")) {
    // 命中
}

// 取值
std::string val;
if (cache.get("key1", val)) {
    std::cout << val << std::endl;
}

// 删除
cache.remove("key1");

// 清空
cache.clear();

3.3 集成到现有工程

仅需将 SimpleCache.h 和 SimpleCache.cpp 加入编译列表,无任何第三方库依赖。

4. 测试

4.1 编译

g++ -std=c++11 -pthread -o test_cache SimpleCache.cpp test_cache.cpp

编译选项说明:

  • -std=c++11:使用 C++11 标准(chrono、thread、atomic 等)
  • -pthread:链接 pthread 库(后台清理线程)

4.2 测试用例

类别用例验证点
基本功能set / exists写入后可查询,不存在 key 返回 false
基本功能get 取值正确返回写入的值
基本功能remove 删除删除后不可查询,删除不存在 key 不崩溃
基本功能覆盖写入相同 key 多次写入取最新值
过期机制惰性过期1 秒 TTL,等待 1.1 秒后 exists 返回 false
过期机制手动清理cleanup() 正确清理全部过期条目并返回清理数
过期机制自定义 TTL短 TTL 项过期、长 TTL 项未过期
过期机制后台定期清理1 秒过期 + 1 秒清理间隔,2.5 秒后 size 归零
缓存管理clear 清空清空后 size 为 0
线程安全并发读写10 线程各写 100 条 + 10 线程各读 100 条,无崩溃无数据竞争

4.3 运行结果

$ ./test_cache
========================================
  SimpleCache 单元测试
========================================

[1] 基本功能
----------------------------------------
  [TEST] set / exists 基本功能 ... PASSED
  [TEST] get 取值 ... PASSED
  [TEST] remove 删除 ... PASSED
  [TEST] 覆盖写入 ... PASSED
  [TEST] clear 清空 ... PASSED

[2] 过期机制
----------------------------------------
  [TEST] 惰性过期 ... PASSED
  [TEST] 手动清理过期条目 ... PASSED
  [TEST] 自定义 TTL ... PASSED
  [TEST] 后台线程定期清理 ... PASSED

[3] 多字段组合 key
----------------------------------------
  [TEST] 多字段组合 key(门架+车牌) ... PASSED
  [TEST] 多字段 key 过期 ... PASSED
  [TEST] 三字段组合 key ... PASSED

[4] 多线程安全
----------------------------------------
  [TEST] 多线程安全 ... (hits=1000) PASSED

========================================
  结果: 13 通过, 0 失败
========================================

5. 小结

本文设计并实现了一款基于 C++11 标准库的轻量级内存缓存器,具有以下特点:

优势:

  • 零依赖:仅使用 C++ 标准库(unordered_map、mutex、chrono、thread、atomic),无需 Redis 或任何第三方库
  • 线程安全:std::mutex + lock_guard 保证并发场景下数据一致性
  • 双过期机制:惰性删除保证查询即时性,后台线程防止内存堆积
  • 接口简洁:set / get / exists / remove / clear 覆盖常规缓存操作

局限与改进方向:

  • 当前不支持 LRU / LFU 等淘汰策略,在高容量场景下可考虑增加容量上限和淘汰逻辑
  • std::mutex 在极高并发读场景下存在性能瓶颈,可升级为读写锁(std::shared_mutex,C++17)
  • cleanup() 全量遍历 O(n),条目数极大时可改为分段扫描
  • key 当前仅支持字符串类型,可模板化以支持泛型 key

适用场景:
嵌入式部署、边缘计算节点、以及在不想引入 Redis 等外部中间件的情况下需要一个小型本地缓存的 C++ 后台服务。

附录:源码

A.1 头文件 SimpleCache.h

#ifndef _SIMPLECACHE_H_
#define _SIMPLECACHE_H_

#include <string>
#include <unordered_map>
#include <mutex>
#include <chrono>
#include <thread>
#include <atomic>
#include <functional>

/**
 * @brief 轻量级内存缓存器
 * 
 * 线程安全,支持:
 *   - 添加 / 查询 / 删除
 *   - 可配置过期时间(默认 10 分钟)
 *   - 惰性过期 + 后台定期清理
 *   - 无外部依赖(纯 C++11 标准库)
 */
class SimpleCache
{
public:
    /**
     * @param expireSeconds  默认过期时间(秒),默认 600 秒 = 10 分钟
     * @param cleanupInterval 后台清理间隔(秒),默认 60 秒;设为 0 则仅惰性过期
     */
    explicit SimpleCache(int expireSeconds = 600, int cleanupInterval = 60);

    ~SimpleCache();

    // 禁止拷贝
    SimpleCache(const SimpleCache&) = delete;
    SimpleCache& operator=(const SimpleCache&) = delete;

    /**
     * @brief 添加/更新缓存项
     * @param key   缓存键(如:门架HEX + 车牌号)
     * @param value 缓存值(可为空串,仅标记存在)
     * @param ttl   该项的过期时间(秒),0 表示使用默认值
     */
    void set(const std::string& key, const std::string& value = "", int ttl = 0);

    /**
     * @brief 查询缓存是否存在(未过期)
     * @return true 存在且未过期, false 不存在或已过期
     */
    bool exists(const std::string& key);

    /**
     * @brief 获取缓存值
     * @param key   缓存键
     * @param value [out] 缓存值
     * @return true 命中, false 未命中或已过期
     */
    bool get(const std::string& key, std::string& value);

    /**
     * @brief 删除指定缓存项
     */
    void remove(const std::string& key);

    /**
     * @brief 清空全部缓存
     */
    void clear();

    /**
     * @brief 获取当前缓存项数量(含已过期但尚未清理的)
     */
    size_t size();

    /**
     * @brief 手动触发一次过期清理
     * @return 清理掉的条目数
     */
    size_t cleanup();

    /**
     * @brief 修改默认过期时间(秒)
     */
    void setDefaultTTL(int seconds);

private:
    struct CacheEntry
    {
        std::string value;
        std::chrono::steady_clock::time_point expireAt;
    };

    std::unordered_map<std::string, CacheEntry> m_cache;
    std::mutex m_mutex;

    int m_defaultTTL;           // 默认过期时间(秒)
    int m_cleanupInterval;      // 后台清理间隔(秒)
    std::atomic<bool> m_running;
    std::thread m_cleanupThread;

    void cleanupLoop();
    bool isExpired(const CacheEntry& entry);
};

#endif // _SIMPLECACHE_H_

A.2 实现文件SimpleCache.cpp

#include "SimpleCache.h"
#include <algorithm>

// ============================================================
// SimpleCache 实现
// ============================================================

SimpleCache::SimpleCache(int expireSeconds, int cleanupInterval)
    : m_defaultTTL(expireSeconds)
    , m_cleanupInterval(cleanupInterval)
    , m_running(false)
{
    if (m_cleanupInterval > 0)
    {
        m_running = true;
        m_cleanupThread = std::thread(&SimpleCache::cleanupLoop, this);
    }
}

SimpleCache::~SimpleCache()
{
    m_running = false;
    if (m_cleanupThread.joinable())
    {
        m_cleanupThread.join();
    }
}

void SimpleCache::set(const std::string& key, const std::string& value, int ttl)
{
    int effectiveTTL = (ttl > 0) ? ttl : m_defaultTTL;

    CacheEntry entry;
    entry.value = value;
    entry.expireAt = std::chrono::steady_clock::now()
                     + std::chrono::seconds(effectiveTTL);

    std::lock_guard<std::mutex> lock(m_mutex);
    m_cache[key] = std::move(entry);
}

bool SimpleCache::exists(const std::string& key)
{
    std::lock_guard<std::mutex> lock(m_mutex);

    auto it = m_cache.find(key);
    if (it == m_cache.end())
    {
        return false;
    }

    if (isExpired(it->second))
    {
        m_cache.erase(it);
        return false;
    }

    return true;
}

bool SimpleCache::get(const std::string& key, std::string& value)
{
    std::lock_guard<std::mutex> lock(m_mutex);

    auto it = m_cache.find(key);
    if (it == m_cache.end())
    {
        return false;
    }

    if (isExpired(it->second))
    {
        m_cache.erase(it);
        return false;
    }

    value = it->second.value;
    return true;
}

void SimpleCache::remove(const std::string& key)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    m_cache.erase(key);
}

void SimpleCache::clear()
{
    std::lock_guard<std::mutex> lock(m_mutex);
    m_cache.clear();
}

size_t SimpleCache::size()
{
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_cache.size();
}

size_t SimpleCache::cleanup()
{
    std::lock_guard<std::mutex> lock(m_mutex);

    size_t removed = 0;
    for (auto it = m_cache.begin(); it != m_cache.end(); )
    {
        if (isExpired(it->second))
        {
            it = m_cache.erase(it);
            ++removed;
        }
        else
        {
            ++it;
        }
    }
    return removed;
}

void SimpleCache::setDefaultTTL(int seconds)
{
    m_defaultTTL = seconds;
}

void SimpleCache::cleanupLoop()
{
    while (m_running)
    {
        // 以 1 秒粒度检查是否该执行清理
        for (int i = 0; i < m_cleanupInterval && m_running; ++i)
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
        if (m_running)
        {
            cleanup();
        }
    }
}

bool SimpleCache::isExpired(const CacheEntry& entry)
{
    return std::chrono::steady_clock::now() >= entry.expireAt;
}



A.3 测试文件test_cache.cpp

#include "SimpleCache.h"
#include <iostream>
#include <cassert>
#include <thread>
#include <vector>
#include <sstream>

// 简单测试宏
static int g_passed = 0;
static int g_failed = 0;

#define TEST(name) \
    do { \
        std::cout << "  [TEST] " << name << " ... "; \
    } while(0)

#define PASS() \
    do { \
        std::cout << "PASSED" << std::endl; \
        ++g_passed; \
    } while(0)

#define CHECK(cond) \
    do { \
        if (!(cond)) { \
            std::cout << "FAILED (" << __LINE__ << ")" << std::endl; \
            ++g_failed; \
            return; \
        } \
    } while(0)

// ============================================================
// SimpleCache 单元测试
// ============================================================

void test_set_and_exists()
{
    TEST("set / exists 基本功能");

    SimpleCache cache(600, 0); // 惰性过期,无后台线程
    cache.set("key1", "val1");
    cache.set("key2");

    CHECK(cache.exists("key1") == true);
    CHECK(cache.exists("key2") == true);
    CHECK(cache.exists("key3") == false);

    PASS();
}

void test_get()
{
    TEST("get 取值");

    SimpleCache cache(600, 0);
    cache.set("k", "hello world");

    std::string v;
    CHECK(cache.get("k", v) == true);
    CHECK(v == "hello world");

    // 不存在的 key
    CHECK(cache.get("no_such_key", v) == false);

    PASS();
}

void test_remove()
{
    TEST("remove 删除");

    SimpleCache cache(600, 0);
    cache.set("k1");
    cache.set("k2");

    cache.remove("k1");
    CHECK(cache.exists("k1") == false);
    CHECK(cache.exists("k2") == true);

    // 删除不存在的 key 不崩溃
    cache.remove("k999");

    PASS();
}

void test_overwrite()
{
    TEST("覆盖写入");

    SimpleCache cache(600, 0);
    cache.set("k", "old");
    cache.set("k", "new");

    std::string v;
    cache.get("k", v);
    CHECK(v == "new");

    PASS();
}

void test_expire_lazy()
{
    TEST("惰性过期");

    SimpleCache cache(1, 0); // 1 秒过期
    cache.set("k");

    CHECK(cache.exists("k") == true);

    // 等待过期
    std::this_thread::sleep_for(std::chrono::milliseconds(1100));

    // 访问时触发惰性删除
    CHECK(cache.exists("k") == false);

    PASS();
}

void test_manual_cleanup()
{
    TEST("手动清理过期条目");

    SimpleCache cache(1, 0); // 1 秒过期
    cache.set("a");
    cache.set("b");
    cache.set("c");

    std::this_thread::sleep_for(std::chrono::milliseconds(1100));

    size_t before = cache.size();
    CHECK(before == 3); // 尚未清理,size 仍为 3

    size_t removed = cache.cleanup();
    CHECK(removed == 3);
    CHECK(cache.size() == 0);

    PASS();
}

void test_clear()
{
    TEST("clear 清空");

    SimpleCache cache(600, 0);
    cache.set("a");
    cache.set("b");
    cache.set("c");
    CHECK(cache.size() == 3);

    cache.clear();
    CHECK(cache.size() == 0);

    PASS();
}

void test_custom_ttl()
{
    TEST("自定义 TTL");

    SimpleCache cache(600, 0);

    cache.set("short", "", 1);   // 1 秒过期
    cache.set("long",  "", 600); // 10 分钟过期

    CHECK(cache.exists("short") == true);
    CHECK(cache.exists("long") == true);

    std::this_thread::sleep_for(std::chrono::milliseconds(1100));

    CHECK(cache.exists("short") == false);
    CHECK(cache.exists("long") == true);

    PASS();
}

void test_thread_safety()
{
    TEST("多线程安全");

    SimpleCache cache(600, 0);
    const int N = 1000;
    std::vector<std::thread> threads;

    // 多线程并发写入
    for (int i = 0; i < 10; ++i)
    {
        threads.emplace_back([&cache, i, N]() {
            for (int j = 0; j < N / 10; ++j)
            {
                std::ostringstream oss;
                oss << "t" << i << "_" << j;
                cache.set(oss.str(), "x");
            }
        });
    }

    for (auto& t : threads) t.join();

    // 最多 N 条(可能有覆盖的)
    CHECK(cache.size() <= (size_t)N);

    // 多线程并发读取
    threads.clear();
    std::atomic<int> hits{0};
    for (int i = 0; i < 10; ++i)
    {
        threads.emplace_back([&cache, &hits, N]() {
            for (int j = 0; j < N / 10; ++j)
            {
                std::ostringstream oss;
                oss << "t0_" << j;
                if (cache.exists(oss.str()))
                    ++hits;
            }
        });
    }
    for (auto& t : threads) t.join();

    std::cout << "(hits=" << hits << ") ";
    PASS();
}

void test_background_cleanup()
{
    TEST("后台线程定期清理");

    SimpleCache cache(1, 1); // 1 秒过期 + 1 秒清理间隔
    cache.set("bg1");
    cache.set("bg2");

    // 等待后台线程清理
    std::this_thread::sleep_for(std::chrono::milliseconds(2500));

    // 后台清理后,size 应为 0
    CHECK(cache.size() == 0);

    PASS();
}

// ============================================================
// 多字段组合 key 场景测试
// ============================================================

// 工具函数:将多个字段组装为缓存 key,分隔符 '|'
static std::string makeKey(const std::string& a, const std::string& b)
{
    return a + "|" + b;
}

static std::string makeKey(const std::string& a, const std::string& b,
                           const std::string& c)
{
    return a + "|" + b + "|" + c;
}

void test_multi_field_key()
{
    TEST("多字段组合 key(门架+车牌)");

    SimpleCache cache(600, 0);

    // 标记车辆通过门架
    cache.set(makeKey("01G001", "京A12345"));
    cache.set(makeKey("01G001", "京B67890"));
    cache.set(makeKey("02G002", "京A12345"));

    // 查询命中
    CHECK(cache.exists(makeKey("01G001", "京A12345")) == true);
    CHECK(cache.exists(makeKey("01G001", "京B67890")) == true);
    CHECK(cache.exists(makeKey("02G002", "京A12345")) == true);

    // 查询未命中
    CHECK(cache.exists(makeKey("01G001", "京C00000")) == false);
    CHECK(cache.exists(makeKey("03G003", "京A12345")) == false);

    // 删除
    cache.remove(makeKey("01G001", "京A12345"));
    CHECK(cache.exists(makeKey("01G001", "京A12345")) == false);
    CHECK(cache.exists(makeKey("01G001", "京B67890")) == true);

    CHECK(cache.size() == 2);

    PASS();
}

void test_multi_field_key_expire()
{
    TEST("多字段 key 过期");

    SimpleCache cache(1, 0);
    cache.set(makeKey("01G001", "京A12345"));

    CHECK(cache.exists(makeKey("01G001", "京A12345")) == true);

    std::this_thread::sleep_for(std::chrono::milliseconds(1100));

    CHECK(cache.exists(makeKey("01G001", "京A12345")) == false);

    PASS();
}

void test_three_field_key()
{
    TEST("三字段组合 key");

    SimpleCache cache(600, 0);

    std::string k1 = makeKey("01G001", "京A12345", "etc");
    std::string k2 = makeKey("01G001", "京A12345", "cpc");
    std::string k3 = makeKey("02G002", "京A12345", "etc");

    cache.set(k1);
    cache.set(k2);
    cache.set(k3);

    CHECK(cache.exists(k1) == true);
    CHECK(cache.exists(k2) == true);
    CHECK(cache.exists(k3) == true);

    // 不同字段组合不会碰撞
    CHECK(cache.exists(makeKey("01G001", "京A12345")) == false);
    CHECK(cache.size() == 3);

    PASS();
}

// ============================================================
// 主函数
// ============================================================

int main()
{
    std::cout << "========================================" << std::endl;
    std::cout << "  SimpleCache 单元测试" << std::endl;
    std::cout << "========================================" << std::endl;

    std::cout << std::endl << "[1] 基本功能" << std::endl;
    std::cout << "----------------------------------------" << std::endl;
    test_set_and_exists();
    test_get();
    test_remove();
    test_overwrite();
    test_clear();

    std::cout << std::endl << "[2] 过期机制" << std::endl;
    std::cout << "----------------------------------------" << std::endl;
    test_expire_lazy();
    test_manual_cleanup();
    test_custom_ttl();
    test_background_cleanup();

    std::cout << std::endl << "[3] 多字段组合 key" << std::endl;
    std::cout << "----------------------------------------" << std::endl;
    test_multi_field_key();
    test_multi_field_key_expire();
    test_three_field_key();

    std::cout << std::endl << "[4] 多线程安全" << std::endl;
    std::cout << "----------------------------------------" << std::endl;
    test_thread_safety();

    std::cout << std::endl << "========================================" << std::endl;
    std::cout << "  结果: " << g_passed << " 通过, "
              << g_failed << " 失败" << std::endl;
    std::cout << "========================================" << std::endl;

    return (g_failed == 0) ? 0 : 1;
}

以上就是基于C++实现内存缓存器的具体方案的详细内容,更多关于C++内存缓存器实现方案的资料请关注脚本之家其它相关文章!

相关文章

  • c++下迭代器总结

    c++下迭代器总结

    大家好,本篇文章主要讲的是c++下迭代器总结,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • Qt实现实时鼠标绘制图形

    Qt实现实时鼠标绘制图形

    这篇文章主要介绍了Qt中QGraphicsView架构下如何实现实时鼠标绘制图形,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起动手试一试
    2022-02-02
  • C语言中获取文件状态的相关函数小结

    C语言中获取文件状态的相关函数小结

    这篇文章主要介绍了C语言中获取文件状态的相关函数小结,包括stat()函数和fstat()函数以及lstat()函数的使用,需要的朋友可以参考下
    2015-09-09
  • 用C语言实现简易通讯录

    用C语言实现简易通讯录

    这篇文章主要为大家详细介绍了用C语言实现简易通讯录,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • Qt6.0开发环境搭建步骤(图文)

    Qt6.0开发环境搭建步骤(图文)

    这篇文章主要介绍了Qt6.0开发环境搭建步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • C/C++中字符串的存储方式详解

    C/C++中字符串的存储方式详解

    在C语言中,字符串通常以字符数组的形式存在,并以空字符(\0)结尾,在C++中,建议使用 std::string 类来处理字符串,本文给大家介绍了C/C++中字符串的存储方式,需要的朋友可以参考下
    2024-11-11
  • C语言中的直接插入排序(带图详细)

    C语言中的直接插入排序(带图详细)

    这篇文章主要介绍了C语言中的直接插入排序(带图详细),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • 基于C++全局变量的声明与定义的详解

    基于C++全局变量的声明与定义的详解

    本篇文章是对C++全局变量的声明与定义进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • Visual Studio 2022最新版安装教程(图文详解)

    Visual Studio 2022最新版安装教程(图文详解)

    本文主要介绍了Visual Studio 2022最新版安装教程,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • C++继承详细介绍

    C++继承详细介绍

    我们都知道面向对象语言的三大特点是:**封装,继承,多态。**之前在类和对象部分,我们提到了C++中的封装,那么今天呢,我们来学习一下C++中的继承
    2022-10-10

最新评论