用std::function实现Command更轻量,因其无需定义命令类继承体系,可直接捕获lambda、函数或成员函数;可撤销Command需同时存储execute_fn和undo_fn两个std::function,绑定成员函数推荐用lambda并注意捕获方式。

为什么用 std::function 实现 Command 比虚函数更轻量?
因为不用定义一堆具体命令类和继承体系,std::function 能直接捕获 lambda、普通函数或成员函数,把“执行逻辑”变成一等公民。适合配置驱动、脚本化或临时任务调度场景,比如 UI 按钮绑定、宏录制回放、Undo 栈的简单实现。
但要注意:它不自带 undo() 或元信息(如命令名),若需这些,得额外包装一层结构体。
如何构造一个可撤销的 Command 类?
用 std::function 存执行逻辑,再加一个 std::function 存撤销逻辑。两者都支持捕获上下文,避免裸指针生命周期风险。
- 撤销函数必须与执行函数语义对称,例如执行时
balance += 100,撤销时就得balance -= 100 - 避免在 lambda 中捕获局部变量的引用(除非确保生命周期足够长),优先用值捕获或
shared_ptr - 如果撤销逻辑不存在,可设为默认空函数:
std::functionundo_fn = []{};
struct Command {
std::function execute_fn;
std::function undo_fn;
void execute() { execute_fn(); }
void undo() { undo_fn(); }};
立即学习“C++免费学习笔记(深入)”;
怎么把成员函数绑定成 Command?
不能直接传 &MyClass::doSave,必须绑定对象实例。用 std::bind 或 lambda 更直观,lambda 更推荐——类型推导干净、无隐藏拷贝开销。
- 错误写法:
Command cmd{&MyClass::doSave, &MyClass::undoSave}; —— 缺少对象,编译失败
- 正确写法(lambda):
[obj](){ obj.doSave(); },注意值捕获 obj 还是引用捕获 &obj,取决于是否需要修改原对象
- 若对象是动态分配的,建议用
std::shared_ptr 捕获,防止悬垂
class Editor {
public:
void save() { /* ... */ }
void undo_save() { /* ... */ }
};
Editor editor;
auto cmd = Command{
[&editor]() { editor.save(); },
[&editor]() { editor.undo_save(); }
};
执行队列和 Undo 栈怎么配合 std::function?
用 std::vector 当命令历史,每次 execute() 后 push,undo() 时 pop 并调用其 undo_fn。关键点在于:不要在 Command 构造后修改被捕获对象的状态,否则 undo 行为不可预测。
常见坑是多次 undo 后又 redo —— std::function 版本默认不支持 redo,除非你额外维护一个重做栈,或让每个 Command 自带 redo_fn 字段。
另外,std::function 有约 16–32 字节的额外开销(取决于实现),高频短命命令(如每帧几十个)要考虑是否值得;此时原始函数指针 + void* 上下文可能更高效。











