C++如何实现备忘录模式 C++备忘录模式的设计

备忘录模式是一种保存和恢复对象状态的设计模式,其核心在于通过备忘录类存储对象状态,发起人类负责创建和恢复状态,管理者类用于管理多个备忘录。1. 使用模板实现通用备忘录类,避免类型限制;2. 采用智能指针(如 std::shared_ptr)管理内存,防止内存泄漏;3. 注意深拷贝对象状态,确保备忘录数据独立;4. 在多线程环境下使用互斥锁(std::mutex)保证线程安全;5. 性能优化可通过增量式保存、共享状态、延迟复制或移动语义等方式实现。该模式广泛应用于撤销/重做功能、事务回滚、游戏存档和配置版本管理等场景。

C++如何实现备忘录模式 C++备忘录模式的设计

备忘录模式,简单来说,就是把对象的状态保存下来,以后可以恢复到之前的状态。这在很多场景下都很有用,比如游戏存档、撤销操作等等。C++实现备忘录模式,核心在于定义一个备忘录类,用于存储对象的状态,以及一个发起人类,负责创建和恢复备忘录。

C++如何实现备忘录模式 C++备忘录模式的设计

C++备忘录模式的设计

C++如何实现备忘录模式 C++备忘录模式的设计

如何在C++中设计一个通用的备忘录类?

设计通用的备忘录类,要考虑到不同对象的状态可能差异很大。一种常见的做法是使用void*指针来存储状态,并配合一个类型标识符。但是,这种方式需要手动管理内存,容易出错。更安全的方式是使用模板,让备忘录类可以存储任意类型的数据。

立即学习“C++免费学习笔记(深入)”;

C++如何实现备忘录模式 C++备忘录模式的设计

#include #include template class Memento {public:    Memento(T state) : state_(std::make_shared(state)) {}    std::shared_ptr getState() const {        return state_;    }private:    std::shared_ptr state_;};template class Originator {public:    Originator(T state) : state_(state) {}    void setState(T state) {        state_ = state;        std::cout << "State set to: " << state_ << std::endl;    }    Memento saveStateToMemento() {        std::cout << "Saving state to Memento: " << state_ << std::endl;        return Memento(state_);    }    void getStateFromMemento(Memento memento) {        state_ = *memento.getState();        std::cout << "State after restoring from Memento: " << state_ << std::endl;    }private:    T state_;};class Caretaker {public:    void add(Memento state) {        mementos_.push_back(state);    }    Memento get(int index) const {        return mementos_[index];    }private:    std::vector<Memento> mementos_;};int main() {    Originator originator(1);    Caretaker caretaker;    originator.setState(2);    caretaker.add(originator.saveStateToMemento());    originator.setState(3);    caretaker.add(originator.saveStateToMemento());    originator.setState(4);    std::cout << "Current State: " << 4 << std::endl;    originator.getStateFromMemento(caretaker.get(1));    std::cout << "First saved State: " << originator.getStateFromMemento(caretaker.get(0)) << std::endl;    return 0;}

这段代码展示了如何使用模板来实现一个通用的备忘录模式。 Memento 类存储状态,Originator 类负责设置和恢复状态,Caretaker 类负责管理备忘录。 注意 std::shared_ptr 的使用,避免内存泄漏。

备忘录模式在实际项目中的应用场景有哪些?

备忘录模式在实际项目中有很多应用场景。

撤销/重做功能: 这是最常见的应用。编辑器、绘图软件等,都需要支持撤销和重做操作。每次操作前,保存对象的状态到备忘录,撤销时,从备忘录中恢复。事务管理: 数据库事务需要保证原子性。如果事务失败,需要回滚到之前的状态。备忘录模式可以用来保存事务开始前的状态。游戏存档: 游戏玩家可以随时保存游戏进度。备忘录模式可以用来保存游戏状态。配置管理: 应用程序的配置可以有很多版本。备忘录模式可以用来保存不同版本的配置,方便切换。

如何优化C++备忘录模式的性能?

备忘录模式的性能瓶颈主要在于状态的复制。如果对象的状态很大,复制的开销会很大。可以考虑以下优化方法:

增量式备忘录: 只保存状态的变化部分,而不是完整状态。共享状态: 如果多个对象的状态相同,可以共享同一个备忘录。延迟复制: 在真正需要恢复状态时,才进行复制。使用移动语义: C++11引入了移动语义,可以避免不必要的复制。

例如,如果对象的状态是一个很大的数组,可以只保存数组中被修改的部分。

备忘录模式与其他设计模式的区别是什么?

备忘录模式容易与其他设计模式混淆,特别是与命令模式和状态模式。

备忘录模式 vs. 命令模式: 命令模式将请求封装成对象,可以用于记录和撤销操作。备忘录模式则是保存对象的状态,用于恢复。命令模式关注的是操作,备忘录模式关注的是状态。备忘录模式 vs. 状态模式: 状态模式允许对象在内部状态改变时改变它的行为。备忘录模式则是保存对象的状态,用于外部恢复。状态模式关注的是对象内部的状态转换,备忘录模式关注的是对象外部的状态保存。

简单来说,命令模式是“做什么”,备忘录模式是“什么样”,状态模式是“我是谁”。

C++中实现备忘录模式有哪些常见的坑?

在C++中实现备忘录模式,容易遇到一些坑:

内存管理: 如果使用void*指针存储状态,需要手动管理内存,容易出现内存泄漏或野指针。使用智能指针可以避免这个问题。深拷贝 vs. 浅拷贝: 备忘录需要保存对象的状态,必须进行深拷贝,而不是浅拷贝。否则,修改原始对象的状态,会影响备忘录中的状态。线程安全: 如果多个线程同时访问备忘录,需要考虑线程安全问题。可以使用互斥锁来保护备忘录的状态。

例如,如果备忘录类只是简单地复制了对象的指针,而不是复制对象本身,那么修改原始对象的状态,备忘录中的状态也会被修改。这就是浅拷贝的问题。

如何在多线程环境下使用备忘录模式?

在多线程环境下使用备忘录模式,需要保证线程安全。一种常见的做法是使用互斥锁来保护备忘录的状态。

#include #include #include // ... (省略前面的代码)template class Originator {public:    Originator(T state) : state_(state) {}    void setState(T state) {        std::lock_guard lock(mutex_);        state_ = state;        std::cout << "State set to: " << state_ << " by thread " << std::this_thread::get_id() << std::endl;    }    Memento saveStateToMemento() {        std::lock_guard lock(mutex_);        std::cout << "Saving state to Memento: " << state_ << " by thread " << std::this_thread::get_id() << std::endl;        return Memento(state_);    }    void getStateFromMemento(Memento memento) {        std::lock_guard lock(mutex_);        state_ = *memento.getState();        std::cout << "State after restoring from Memento: " << state_ << " by thread " << std::this_thread::get_id() << std::endl;    }private:    T state_;    std::mutex mutex_;};// ... (省略后面的代码)int main() {    Originator originator(1);    Caretaker caretaker;    std::thread t1([&]() {        originator.setState(2);        caretaker.add(originator.saveStateToMemento());    });    std::thread t2([&]() {        originator.setState(3);        caretaker.add(originator.saveStateToMemento());    });    t1.join();    t2.join();    originator.setState(4);    std::cout << "Current State: " << 4 << std::endl;    originator.getStateFromMemento(caretaker.get(0));    return 0;}

这段代码使用 std::mutex 来保护 Originator 类的状态。每个访问状态的方法都需要获取锁,以保证线程安全。 注意,过度使用锁会影响性能,需要根据实际情况进行权衡。

以上就是C++如何实现备忘录模式 C++备忘录模式的设计的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1466569.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月18日 16:08:45
下一篇 2025年12月15日 18:31:53

相关推荐

  • C++异常与返回值错误码如何选择 不同场景下的错误处理方案

    在c++++中,错误处理方式主要有异常和错误码两种,选择取决于具体场景。异常适用于罕见且需立即中断执行的错误,如内存分配失败、文件打开失败、非法参数传入,它使代码更清晰,调用者必须处理错误;错误码适合常见且可预见的错误,如用户输入不合法、网络超时、配置项不存在,通过返回值控制流程,避免性能不确定性和…

    2025年12月18日 好文分享
    000
  • 怎样优化C++中的动态派发 基于标签分发的编译期多态

    标签分发是一种利用编译期类型信息实现多态行为的技术,通过定义空结构体作为标签并结合函数重载解析,在编译时确定具体调用路径;2. 其核心优势包括零运行时开销、极致优化潜力(如函数内联)、静态类型安全、泛型可复用性及清晰的意图表达;3. 实际应用中可结合c++++17的if constexpr进行条件编…

    2025年12月18日 好文分享
    000
  • C++单例模式如何避免双重检查锁定问题 现代C++11原子变量实现方案

    双重检查锁定的问题在于可能因编译器或cpu重排序导致未完全初始化的对象被访问,引发未定义行为。解决方案包括:1. 使用std::atomic和内存顺序控制实现线程安全的单例;2. 采用局部静态变量方式由编译器自动处理同步问题;3. 注意指针管理时的析构清理和不同平台的测试验证。 在C++中实现单例模…

    2025年12月18日 好文分享
    000
  • C++享元模式如何优化内存 共享细粒度对象的内在状态

    享元模式通过分离内在状态与外在状态并共享内在状态来优化内存。其核心在于识别大量重复且不变的内在状态(如字符的字体、大小、颜色),将其封装在享元对象中并通过工厂统一管理,避免重复创建物理对象;外在状态(如字符坐标、是否选中)则由客户端动态传入,不被共享。实现时需注意状态划分、线程安全、内存管理和调试复…

    2025年12月18日 好文分享
    000
  • 怎样处理C++中的环形引用问题 weak_ptr打破循环引用技巧

    环形引用指两个或多个shared_ptr相互引用导致内存泄漏。例如,结构体a和b各自持有对方的shared_ptr,当main函数结束时,它们的引用计数均不为0,无法释放。解决方法是使用weak_ptr打破循环,weak_ptr不会增加引用计数,仅观察对象。其使用步骤包括:1. 将其中一个share…

    2025年12月18日 好文分享
    000
  • C++如何监控文件变化?文件系统观察者模式

    在c++++中监控文件变化的实现方法有三种:windows平台使用readdirectorychangesw、linux平台使用inotify、跨平台可使用boost或第三方库。具体步骤如下:1. windows下通过createfile打开目录并调用readdirectorychangesw监听目…

    2025年12月18日 好文分享
    000
  • C++怎样实现简易记账本 类封装与收支记录管理

    记账本适合用c++++练习类封装与数据管理,核心在于将收支记录抽象为类并合理组织代码结构。1. 设计incomeexpense类表示单条记录,包含金额、类型、日期、分类和备注,并提供访问和显示方法;2. ledger类管理所有记录,支持添加、显示全部、按分类筛选及统计总收入与支出;3. 主程序提供菜…

    2025年12月18日 好文分享
    000
  • C++11的constexpr有什么改进 编译期计算的演进历程

    c++++11的constexpr改进在于允许函数和变量在编译时求值。其主要改进包括:1. constexpr函数支持在编译时执行简单函数,如仅含一个return语句的函数;2. constexpr变量可在编译时初始化并作为常量使用;3. 对函数和变量施加约束以确保编译期可求值。后续标准进一步扩展了…

    2025年12月18日 好文分享
    000
  • C++循环结构有哪几种形式 for while do-while使用场景

    c++++中常见的循环结构主要有三种:for、while和do-while。for循环适合已知循环次数的场景,例如遍历数组或执行固定次数的操作;while循环适用于不知道具体循环次数但有明确结束条件的情况,如等待用户输入或数据读取直到文件结尾;do-while循环与while类似,但至少会执行一次循…

    2025年12月18日 好文分享
    000
  • C++的goto语句应该避免吗 分析goto的使用场景与替代方案

    goto语句在c++++中并非完全不可用,但在大多数情况下应避免使用。1. goto的主要问题在于破坏代码结构,导致程序难以理解和维护;2. 其常见用途包括跳出多层循环、错误处理和状态机实现;3. 然而,这些场景通常都有更优的替代方案,如break/continue、提取函数、return、异常处理…

    2025年12月18日 好文分享
    000
  • C++跨模块异常传递安全吗 动态链接库异常处理注意事项

    跨模块抛异常需谨慎处理,主要原因包括:1.编译器差异导致兼容性问题,不同编译器或设置可能导致异常无法被捕获,建议避免跨模块抛自定义异常,改用返回码和错误描述;2.动态链接库导出函数时异常规范不一致可能引发崩溃,建议在接口层隔离异常并使用返回值传递错误;3.标准库异常也可能因stl实现版本不同而失效,…

    2025年12月18日 好文分享
    000
  • C++如何优化频繁的小内存分配 实现高效内存池的方案与实践

    c++++中优化频繁小内存分配的核心方法是使用自定义内存池。1. 通过预先申请一大块内存并切分为固定大小的小块,避免频繁系统调用;2. 使用空闲列表管理可用内存块,实现快速分配与释放;3. 提高缓存命中率并减少内存碎片;4. 针对多线程场景引入锁或线程局部存储确保线程安全;5. 确保内存对齐以避免性…

    2025年12月18日 好文分享
    000
  • C++如何实现银行账户模拟 类与对象的基础应用案例

    银行账户模拟可通过c++++类和对象实现,并可扩展利息计算、异常处理和继承机制。1. 利息计算通过添加calculateinterest()方法和interestrate属性实现,利息自动存入账户;2. 透支处理可在withdraw()中加入透支限制判断,控制取款额度并提示错误;3. 使用继承可创建…

    2025年12月18日 好文分享
    000
  • C++中的placement new如何使用 特定内存位置构造对象的技术

    placement new 主要用于在指定内存位置构造对象,避免额外内存分配。常见场景包括内存池、嵌入式系统和自定义容器实现。使用步骤:1. 分配原始内存;2. 用 placement new 构造对象;3. 手动调用析构函数;4. 若需释放内存则手动 free。注意事项包括确保内存对齐、手动析构、…

    2025年12月18日 好文分享
    000
  • C++中结构体与类的性能差异 对比内存布局和访问效率

    结构体和类在c++++中的性能差异通常可以忽略不计。1. 内存布局默认相同,但内存对齐、虚函数、继承等因素会影响实际布局,进而可能影响性能;2. 虚函数会引入虚函数表指针(vptr),增加对象大小并降低调用效率;3. 继承会包含基类成员变量,多重继承使布局更复杂;4. 空基类优化(ebo)可减少内存…

    2025年12月18日 好文分享
    000
  • 如何用C++制作密码强度检测器 正则表达式和评分规则

    密码强度检测的核心在于评估密码的复杂性和随机性,用c++++实现的关键是正则表达式的灵活运用和评分规则的合理制定。1. 首先需要一个接收用户输入密码的函数;2. 然后根据长度、字符种类(大写、小写、数字、特殊字符)、常见弱密码模式等进行检查;3. 使用正则表达式快速判断特定类型字符的存在;4. 制定…

    2025年12月18日 好文分享
    000
  • C++17结构化绑定怎么应用 多返回值解构与元组处理实践

    c++++17结构化绑定是一种语法糖,用于将聚合类型(如数组、结构体、std::tuple等)的成员解包为独立变量。1. 其核心语法是auto [变量1, 变量2, …] = 表达式;,适用于解构std::pair和std::tuple、结构体与类、以及数组;2. 它显著提升代码可读性与…

    2025年12月18日 好文分享
    000
  • C++中vector如何动态扩容 容量增长策略和性能影响分析

    std::vec++tor扩容策略通常采用倍增机制以减少频繁内存拷贝带来的性能损耗。例如,gcc下容量按2倍增长,visual studio则多为1.5倍。扩容时会重新分配内存并复制旧数据,导致时间和空间开销。若提前知道元素数量,应使用reserve()预分配内存,避免多次扩容。此外,合理使用shr…

    2025年12月18日 好文分享
    000
  • 什么时候应该使用C++的shared_ptr 解释共享所有权场景下的智能指针选择

    当需要多个指针共享同一个对象的所有权时,应使用 c++++ 的 shared_ptr。shared_ptr 通过引用计数自动管理对象生命周期,确保只要还有一个 shared_ptr 指向对象,就不会被释放;它适用于多线程共享数据、对象拥有关系不明确、观察者模式及资源池等场景;正确使用时应优先用 ma…

    2025年12月18日 好文分享
    000
  • C++如何实现自定义内存管理 重载new和delete操作符实例

    在c++++中,可以通过重载new和delete操作符实现自定义内存管理。1. 在类级别重载时,需在类内定义operator new和operator delete,控制该类对象的内存分配与释放;2. 全局重载则替换整个程序的默认内存分配逻辑,适用于统一监控或替换分配器;3. 必须配对提供new/d…

    2025年12月18日 好文分享
    000

发表回复

登录后才能评论
关注微信