如何从注入的策略模式中调用PIMPL实现类的成员函数
解决方案
你选择的通过std::function传递成员方法的方案是完全可行的,既不会暴露Impl内部接口,也不需要对现有WorldTraversal类做大的重构,只需要修正现有代码的几个语法问题即可:
现有代码的错误点
- 成员函数的
std::bind语法错误:绑定非静态成员函数时,第一个参数需要是成员函数的完整地址,第二个参数是对应类实例的指针/引用 setStrategy方法参数类型不匹配:入参声明为ITraversalStrategy右值引用,但实际传入的是std::unique_ptr<ITraversalStrategy>- 缺少
ITraversalStrategy基类定义,且基类没有声明虚析构函数会导致内存释放异常 - 类定义末尾缺少分号等基础语法问题
修正后的可运行代码
#include <memory> #include <functional> // 先声明策略基类 class ITraversalStrategy { public: using ProcessPath = std::function<void()>; using ExecutePath = std::function<void()>; virtual void apply(ProcessPath processPath, ExecutePath executePath) = 0; virtual ~ITraversalStrategy() = default; // 必须加虚析构避免内存泄漏 }; class WorldTraversal { public: // 修正参数类型,接收unique_ptr<策略> void setStrategy(std::unique_ptr<ITraversalStrategy> strategy) { m_impl->traversalStrategy = std::move(strategy); } void execute() { // 修正bind语法:第一个参数是成员函数的完整地址,第二个参数是Impl实例指针 m_impl->traversalStrategy->apply( std::bind(&WorldTraversal::Impl::processPath, m_impl.get()), std::bind(&WorldTraversal::Impl::executePath, m_impl.get()) ); } WorldTraversal(); // 需显式声明构造析构,适配PIMPL的 unique_ptr 要求 ~WorldTraversal(); private: struct Impl; std::unique_ptr<Impl> m_impl; }; // IMPL定义 struct WorldTraversal::Impl { void processPath(); void executePath(); std::unique_ptr<ITraversalStrategy> traversalStrategy; }; // 实现WorldTraversal的构造和析构,放在Impl定义之后 WorldTraversal::WorldTraversal() : m_impl(std::make_unique<Impl>()) {} WorldTraversal::~WorldTraversal() = default; // 实现Impl的方法 void WorldTraversal::Impl::processPath() {} void WorldTraversal::Impl::executePath() {} // 具体策略实现 class GroundTraversal : public ITraversalStrategy { public: void apply(ProcessPath processPath, ExecutePath executePath) override { processPath(); executePath(); } }; int main() { WorldTraversal worldTraversal; worldTraversal.setStrategy(std::make_unique<GroundTraversal>()); worldTraversal.execute(); return 0; }
方案说明
- 传递
std::function的方式不会暴露Impl的任何内部细节,策略侧只能拿到两个可调用对象,完全不知道其内部关联的是Impl的成员函数,符合接口隔离的要求 - 不需要给策略类增加友元声明,也不需要修改
WorldTraversal的公共接口,适配你不能大量重构现有类的限制 std::bind绑定unique_ptr的原始指针是安全的:apply方法是在WorldTraversal::execute的调用栈内同步执行,执行过程中m_impl肯定是存活状态,不会出现悬垂指针问题
内容的提问来源于stack exchange,提问作者FMunkee
相关产品推荐
相关产品推荐

