无需转发函数,为已有C++类适配接口的实现方案
在C++中无需手动转发函数适配已有类到接口的优雅方案
在C#中,我们可以通过让派生类同时继承已有类和接口,无需修改原类就能完成接口适配,实现非常简洁:
interface IPermissions { bool CanView(); bool CanUpdate(); bool CanDelete(); } class Concrete { public bool CanView() { return true; } public bool CanUpdate() { return true; } public bool CanDelete() { return true; } } class Record : Concrete, IPermissions { }
之后就能直接将Record实例赋值给接口类型并调用方法:
IPermissions record = new Record(); record.CanView();
但在C++中,由于缺乏类似的隐式接口适配机制,通常只能手动编写转发函数来实现:
class IPermissions { public: virtual bool CanView() = 0; virtual bool CanUpdate() = 0; virtual bool CanDelete() = 0; }; class Concrete { public: bool CanView() { return true; } bool CanUpdate() { return true; } bool CanDelete() { return true; } }; class Record : public Concrete, public IPermissions { public: bool CanView() override { return Concrete::CanView(); } bool CanUpdate() override { return Concrete::CanUpdate(); } bool CanDelete() override { return Concrete::CanDelete(); } };
当无法修改Concrete类(比如属于第三方维护的代码)时,手动编写转发不仅繁琐,还容易因遗漏方法导致错误。以下是两种无需手动编写转发的优雅实现方案:
方案一:固定接口的模板适配器
编写针对IPermissions接口的模板适配器类,自动生成转发逻辑,避免重复代码:
// 适配IPermissions接口的模板类 template<typename Impl> class PermissionsAdapter : public IPermissions, public Impl { public: // 继承实现类的所有构造函数 using Impl::Impl; // 自动转发接口方法到实现类 bool CanView() override { return Impl::CanView(); } bool CanUpdate() override { return Impl::CanUpdate(); } bool CanDelete() override { return Impl::CanDelete(); } }; // 使用示例 int main() { PermissionsAdapter<Concrete> record; IPermissions* ptr = &record; ptr->CanView(); return 0; }
这种方案和原问题中Record类的结构一致,通过多重继承实现,完全贴合C#的适配逻辑,且无需手动编写每个方法的转发代码。
方案二:成员持有式适配器(避免多重继承)
如果担心多重继承带来的潜在问题,可以将实现类作为成员持有,结合C++20的std::bind_front绑定方法:
#include <functional> #include <utility> class IPermissions { public: virtual bool CanView() = 0; virtual bool CanUpdate() = 0; virtual bool CanDelete() = 0; virtual ~IPermissions() = default; }; template<typename Impl> class PermissionsAdapter : public IPermissions { private: Impl impl_; // 绑定实现类的方法到函数对象 std::function<bool()> can_view_ = std::bind_front(&Impl::CanView, &impl_); std::function<bool()> can_update_ = std::bind_front(&Impl::CanUpdate, &impl_); std::function<bool()> can_delete_ = std::bind_front(&Impl::CanDelete, &impl_); public: // 转发实现类的构造参数 template<typename... Args> PermissionsAdapter(Args&&... args) : impl_(std::forward<Args>(args)...) {} bool CanView() override { return can_view_(); } bool CanUpdate() override { return can_update_(); } bool CanDelete() override { return can_delete_(); } }; // 使用示例 int main() { PermissionsAdapter<Concrete> record; IPermissions* ptr = &record; ptr->CanView(); return 0; }
这种方式避免了多重继承,同时依然无需手动编写转发逻辑,适合对多重继承有顾虑的场景。
注意事项
- 两种方案都无需修改原有
Concrete类,完全符合需求; - 若接口方法包含参数或非void返回值,只需对应调整适配器中的转发逻辑即可;
- 模板方案编译期完成代码生成,不会带来额外运行时开销。
内容的提问来源于stack exchange,提问作者Kevmeister68
相关产品推荐
相关产品推荐

