如何不使用dynamic_cast或枚举字段,从基类指针区分两个派生类
方案1:访问者模式(兼容C++11及以上,符合开闭原则)
这是纯面向对象的实现方案,不需要手动做类型判断,也不需要依赖RTTI,对原有代码的侵入性很低。
首先前置声明访问者基类,给Base类新增访问者接收虚接口:
class BaseVisitor; class Base { public: bool foo; virtual bool bar() = 0; virtual ~Base() = default; // 新增访问者接收接口 virtual void accept(BaseVisitor& visitor) = 0; };
定义访问者基类,为每个派生类对应一个visit重载:
class BaseVisitor { public: virtual void visit(Derived1& d1) = 0; virtual void visit(Derived2& d2) = 0; virtual ~BaseVisitor() = default; };
两个派生类分别实现accept接口:
class Derived1 : public Base { Derived1(); bool bar() override; void accept(BaseVisitor& visitor) override { visitor.visit(*this); } }; class Derived2 : public Base { Derived2(); std::vector<baz*> bazVector; bool bar() override; void accept(BaseVisitor& visitor) override { visitor.visit(*this); } };
需要访问Derived2的bazVector时,实现对应功能的访问者即可:
class BazVectorProcessor : public BaseVisitor { public: void visit(Derived1& d1) override { // Derived1不需要处理可留空,也可补充对应逻辑 } void visit(Derived2& d2) override { // 直接访问bazVector,无任何类型判断逻辑 for (auto bazPtr : d2.bazVector) { // 自定义处理逻辑 } } };
调用方式非常简单:
BazVectorProcessor processor; for (auto basePtr : mainVector) { basePtr->accept(processor); }
该方案完全符合开闭原则,后续新增派生类只需要补充对应的visit重载即可,不需要修改原有业务逻辑。
方案2:std::variant替代继承(C++17及以上,性能最优)
如果派生类数量固定、后续不会频繁扩展,使用C++17引入的std::variant方案可以完全抛弃继承体系,类型判断在编译期完成,没有虚函数表、RTTI的额外开销。
首先重构类型定义,不需要继承Base类:
#include <variant> #include <vector> class Derived1 { public: bool foo; Derived1(); bool bar(); }; class Derived2 { public: bool foo; Derived2(); std::vector<baz*> bazVector; bool bar(); }; // 用variant封装所有可能的派生类型 using BaseVariant = std::variant<Derived1, Derived2>; // 容器直接存储variant实例 std::vector<BaseVariant> mainVector;
处理时用std::visit匹配类型即可:
for (auto& elem : mainVector) { std::visit([](auto&& arg) { using T = std::decay_t<decltype(arg)>; if constexpr (std::is_same_v<T, Derived2>) { // 直接访问bazVector,类型判断编译期完成 for (auto bazPtr : arg.bazVector) { // 自定义处理逻辑 } } // 公共逻辑可以直接调用,比如arg.bar() }, elem); }
该方案代码更简洁,运行性能高于所有基于继承的实现,是C++17及以上版本的优先选择。
如果仅当前两个派生类、后续无扩展计划,dynamic_cast的方案改动最小,也可以正常使用,不存在明显的设计缺陷。
内容的提问来源于stack exchange,提问作者Артем Налькин
相关产品推荐
相关产品推荐

