如何让派生类调用的继承基类函数适配外部类的派生类指针重载函数?
现有代码
example.h
class Object { public: Object(); void pass_self_to_external_class(); }; class ObjectWithExtraMember : public Object { public: ObjectWithExtraMember(); int extra_member; }; class ExternalClass { public: ExternalClass(); void do_something_with_object(Object* object); void do_something_with_object(ObjectWithExtraMember* object); int extra_member_val; };
example.cpp
#include "example.h" ExternalClass e; Object::Object() {} void Object::pass_self_to_external_class() { e.do_something_with_object(this); } ObjectWithExtraMember::ObjectWithExtraMember() { extra_member = 45; } ExternalClass::ExternalClass() { extra_member_val = 0; } void ExternalClass::do_something_with_object(Object* object) { extra_member_val = 0; } void ExternalClass::do_something_with_object(ObjectWithExtraMember* object) { extra_member_val = object->extra_member; }
问题描述
当ObjectWithExtraMember类的实例调用继承自Object类的pass_self_to_external_class()函数时,编译器会默认匹配ExternalClass中参数为Object*的do_something_with_object重载(因为Object类中的this是Object*类型,重载解析在编译阶段完成)。现在希望无需在派生类中重写pass_self_to_external_class(),就能让该函数传入派生类实例的指针,从而调用参数为ObjectWithExtraMember*的重载版本。
解决方案
方案1:使用CRTP(奇异递归模板模式)实现静态多态
这是最推荐的方案,通过静态绑定在编译阶段确定派生类类型,无运行时开销,也不需要重写函数。
修改Object类为模板类,让派生类继承时传入自身类型:
修改后的example.h
#include <type_traits> // 提前声明外部类与全局实例 class ExternalClass; extern ExternalClass e; template<typename Derived> class Object { public: Object() = default; void pass_self_to_external_class() { // 静态断言确保类型安全,避免错误继承 static_assert(std::is_base_of<Object<Derived>, Derived>::value, "Derived must inherit from Object<Derived>"); e.do_something_with_object(static_cast<Derived*>(this)); } }; class ObjectWithExtraMember : public Object<ObjectWithExtraMember> { public: ObjectWithExtraMember() { extra_member = 45; } int extra_member; }; class ExternalClass { public: ExternalClass() : extra_member_val(0) {} void do_something_with_object(Object<ObjectWithExtraMember>* object); void do_something_with_object(ObjectWithExtraMember* object); int extra_member_val; };
修改后的example.cpp
#include "example.h" ExternalClass e; void ExternalClass::do_something_with_object(Object<ObjectWithExtraMember>* object) { extra_member_val = 0; } void ExternalClass::do_something_with_object(ObjectWithExtraMember* object) { extra_member_val = object->extra_member; }
原理:利用CRTP,Object<Derived>类在编译阶段就能获取派生类Derived的类型,通过static_cast<Derived*>(this)将基类指针转换为派生类指针,此时调用do_something_with_object会直接匹配Derived*对应的重载版本,无需在派生类中重写任何函数。
方案2:动态类型转换(不推荐,仅作补充)
如果无法修改基类的模板化设计,可以通过dynamic_cast结合虚函数实现运行时类型判断,但需要基类包含虚函数(开启RTTI),且存在运行时开销,扩展性较差(新增派生类时需要修改基类函数)。
修改Object类:
class Object { public: Object() = default; // 添加虚析构函数以启用RTTI virtual ~Object() = default; void pass_self_to_external_class() { if (auto derived_obj = dynamic_cast<ObjectWithExtraMember*>(this)) { e.do_something_with_object(derived_obj); } else { e.do_something_with_object(this); } } };
原理:通过dynamic_cast尝试将this转换为ObjectWithExtraMember*,转换成功则调用对应重载,否则调用基类版本。但这种方法需要基类有虚函数,且每次调用都要做运行时类型检查,效率低于CRTP。
内容的提问来源于stack exchange,提问作者Henry Nissen

