如何用单一函数指针存储不同签名函数实现StringList的Undo操作?
解决StringList类Undo操作中函数签名不匹配的问题
针对你遇到的函数指针签名不统一的问题,这里有几个实用的解决方案,不用定义多个函数指针就能搞定:
方案1:用函数对象(仿函数)封装反向操作
定义一个统一的基类作为undo命令的接口,然后为每个操作的反向逻辑创建子类,把需要的参数存在子类对象里,栈里只存基类指针,undo时统一调用无参的execute()方法即可。
示例代码:
#include <stack> #include <memory> #include <vector> #include <string> class StringList; // 统一的undo命令基类 class UndoCommand { public: virtual ~UndoCommand() = default; virtual void execute() = 0; }; // InsertBefore的反向操作:移除插入的元素 class UndoInsertBefore : public UndoCommand { private: StringList& list; int pos; public: UndoInsertBefore(StringList& sl, int p) : list(sl), pos(p) {} void execute() override; }; // RemoveBefore的反向操作:恢复被移除的元素 class UndoRemoveBefore : public UndoCommand { private: StringList& list; int pos; std::string removedStr; public: UndoRemoveBefore(StringList& sl, int p, const std::string& str) : list(sl), pos(p), removedStr(str) {} void execute() override; }; class StringList { private: std::vector<std::string> data; std::stack<std::unique_ptr<UndoCommand>> undoStack; public: void insert_before(int pos, const std::string& str) { data.insert(data.begin() + pos, str); undoStack.push(std::make_unique<UndoInsertBefore>(*this, pos)); } void remove_before(int pos) { std::string removed = data[pos]; data.erase(data.begin() + pos); undoStack.push(std::make_unique<UndoRemoveBefore>(*this, pos, removed)); } void undo() { if (!undoStack.empty()) { undoStack.top()->execute(); undoStack.pop(); } } // 给子类提供访问内部数据的接口 std::vector<std::string>& get_data() { return data; } }; // 实现子类的execute方法 void UndoInsertBefore::execute() { list.get_data().erase(list.get_data().begin() + pos); } void UndoRemoveBefore::execute() { list.get_data().insert(list.get_data().begin() + pos, removedStr); }
这种方式用面向对象的思路把参数和操作绑定,完美统一接口,不用关心每个操作的参数差异。
方案2:用Lambda表达式+std::function(最简洁)
C++11及以后可以用std::function<void()>存储任意可调用对象,包括捕获了参数的lambda。直接把反向操作和需要的参数通过lambda捕获,打包成无参的可调用对象压栈就行,代码量少很多。
示例代码:
#include <stack> #include <functional> #include <vector> #include <string> class StringList { private: std::vector<std::string> data; std::stack<std::function<void()>> undoStack; public: void insert_before(int pos, const std::string& str) { data.insert(data.begin() + pos, str); // 捕获当前对象和插入位置,打包成无参的undo操作 undoStack.push([this, pos]() { data.erase(data.begin() + pos); }); } void remove_before(int pos) { std::string removed = data[pos]; int original_pos = pos; data.erase(data.begin() + pos); // 捕获需要恢复的参数,打包成无参操作 undoStack.push([this, original_pos, removed]() { data.insert(data.begin() + original_pos, removed); }); } void undo() { if (!undoStack.empty()) { undoStack.top()(); undoStack.pop(); } } };
这个方案不用写额外的类,直接用lambda把参数和操作绑定,非常直观简洁。
方案3:用void*存储参数+统一处理函数(不推荐)
如果不能用C++11特性,也可以用结构体存函数指针和void*类型的参数,处理时强制转换类型。但这种方式类型不安全,还要手动管理内存,容易出bug,仅作为备选:
#include <stack> #include <vector> #include <string> class StringList; typedef void (*UndoFunc)(StringList*, void*); struct UndoNode { UndoFunc func; void* args; }; void undo_insert(StringList* list, void* args); void undo_remove(StringList* list, void* args); class StringList { private: std::vector<std::string> data; std::stack<UndoNode> undoStack; public: void insert_before(int pos, const std::string& str) { data.insert(data.begin() + pos, str); int* arg = new int(pos); undoStack.push({undo_insert, arg}); } void remove_before(int pos) { std::string removed = data[pos]; struct RemoveArgs { int pos; std::string str; }; RemoveArgs* ra = new RemoveArgs{pos, removed}; data.erase(data.begin() + pos); undoStack.push({undo_remove, ra}); } void undo() { if (!undoStack.empty()) { UndoNode node = undoStack.top(); node.func(this, node.args); undoStack.pop(); } } std::vector<std::string>& get_data() { return data; } }; void undo_insert(StringList* list, void* args) { int* pos = static_cast<int*>(args); list->get_data().erase(list->get_data().begin() + *pos); delete pos; } void undo_remove(StringList* list, void* args) { struct RemoveArgs { int pos; std::string str; }; RemoveArgs* ra = static_cast<RemoveArgs*>(args); list->get_data().insert(list->get_data().begin() + ra->pos, ra->str); delete ra; }
内容的提问来源于stack exchange,提问作者Default
相关产品推荐
相关产品推荐

