如何让函数调用子类Disc的关联函数而非父类Object的函数?
物理引擎碰撞检测多态调用问题解决方案
问题背景
开发学校作业的简单物理引擎,类继承链为Particle → Object → Disc。PhysicsWorld通过Object*列表遍历处理碰撞,全局声明了intersects(Object, Object)和getPenDist(Object, Object)函数,但实际使用Disc实例时,调用的仍是Object版本的函数,而非Disc的重载版本。且无法将这些函数设为Object的成员函数(因为Disc版本的函数参数为Disc,无法覆盖Object的成员函数)。
解决方案1:RTTI类型判断结合重载
适合作业场景,实现简单。核心是通过dynamic_cast判断对象实际类型,调用对应版本的函数。
修改PhysicsWorld.cpp中的resolveCollisions函数:
#include "PhysicsWorld.h" #include "Disc.h" // 需要引入Disc头文件 void PhysicsWorld::resolveCollisions(float duration) { std::vector<Object*>::iterator iter, iter2; for (iter = objectList.begin(); iter != objectList.end(); ++iter) { for (iter2 = objectList.begin(); iter2 != objectList.end(); ++iter2) { if (*iter == *iter2) continue; bool collision = false; float penDist = 0.0f; // 判断两个对象是否都是Disc类型 Disc* d1 = dynamic_cast<Disc*>(*iter); Disc* d2 = dynamic_cast<Disc*>(*iter2); if (d1 && d2) { // 调用Disc版本的碰撞检测函数 collision = intersects(*d1, *d2); penDist = getPenDist(*d1, *d2); } else { // 调用默认的Object版本函数 collision = intersects(**iter, **iter2); penDist = getPenDist(**iter, **iter2); } if (collision) { // 此处编写碰撞处理逻辑,使用penDist } } } }
注意:编译时需确保开启RTTI(多数编译器默认开启,如GCC需添加
-frtti编译选项)。
解决方案2:双重分发(面向对象优雅实现)
适合后续扩展更多形状(如矩形、三角形)的场景,完全基于虚函数实现多态,无需全局函数。
步骤1:修改Object.h,添加纯虚分发函数
#include <iostream> #include "./Particle.h" // 前置声明Disc类,避免循环引用 class Disc; class Object : public Particle { public: // 核心分发函数:接收任意Object类型的碰撞检测请求 virtual bool intersectsWith(Object& other) = 0; virtual float getPenDistWith(Object& other) = 0; // 针对Disc类型的碰撞检测接口,子类需实现 virtual bool intersectsWith(Disc& other) = 0; virtual float getPenDistWith(Disc& other) = 0; }; // 保留原全局函数作为 fallback(可选) bool intersects(Object o1, Object o2); float getPenDist(Object o1, Object o2);
步骤2:修改Disc.h并实现分发函数
#include "./Object.h" class Disc : public Object { protected: float radius; public: float getRadius() { return radius; } // 第一次分发:将请求转发给other的Disc版本函数 bool intersectsWith(Object& other) override { return other.intersectsWith(*this); } float getPenDistWith(Object& other) override { return other.getPenDistWith(*this); } // 实现Disc与Disc的碰撞检测逻辑 bool intersectsWith(Disc& other) override { return (other.getPosition() - getPosition()).getNorm() < getRadius() + other.getRadius(); } float getPenDistWith(Disc& other) override { return -(other.getPosition() - getPosition()).getNorm() + getRadius() + other.getRadius(); } };
步骤3:修改PhysicsWorld.cpp的碰撞处理逻辑
#include "PhysicsWorld.h" void PhysicsWorld::resolveCollisions(float duration) { std::vector<Object*>::iterator iter, iter2; for (iter = objectList.begin(); iter != objectList.end(); ++iter) { for (iter2 = objectList.begin(); iter2 != objectList.end(); ++iter2) { if (*iter == *iter2) continue; if ((*iter)->intersectsWith(**iter2)) { float penDist = (*iter)->getPenDistWith(**iter2); // 此处编写碰撞处理逻辑 } } } }
这种方式无需全局函数,后续添加新形状时,只需在Object中新增对应形状的纯虚接口,再在所有子类中实现即可,扩展性极强。
内容的提问来源于stack exchange,提问作者Gabriel
相关产品推荐
相关产品推荐

