Box2D封装为RigidBody类后与std::vector搭配时erase操作行为异常
问题根源
你遇到的异常行为是C++「三/五法则」的典型触发场景,核心原因是你的RigidBody类只实现了拷贝构造函数和析构函数,没有实现拷贝赋值运算符,编译器自动生成的默认拷贝赋值是纯浅拷贝,直接复制m_body、m_fixture两个指针的值,完全没有处理底层Box2D物理对象的生命周期,和std::vector的erase逻辑适配就出了问题:std::vector执行erase操作时的逻辑是:将被删除位置之后的所有元素,依次向前拷贝赋值覆盖前一个位置的元素,最后销毁vector末尾的冗余元素。举个例子:假设你的vector有[obj0, obj1, obj2, obj3]四个元素,调用erase(begin())删除obj0时,实际执行步骤是:
- 用obj1赋值给obj0的内存位置
- 用obj2赋值给obj1的内存位置
- 用obj3赋值给obj2的内存位置
- 调用obj3的析构函数,删除末尾元素
默认浅拷贝赋值会直接把obj1的m_body指针覆盖给原来的obj0,obj0原本持有的Box2D Body直接泄漏,最后析构obj3的时候销毁了obj3对应的物理体,所以你看起来就像「删除第一个元素,实际消失的是最后一个」。
第二次执行erase操作时,vector末尾元素的m_body已经是被销毁过的野指针,析构时调用Runtime::PhysicsWorld.DestroyBody访问野指针自然触发崩溃。
解决方案
你可以根据自己的使用场景选任意一种方案修复:
方案1:禁止值语义(最推荐,性能最优)
Box2D的Body本身就不应该随意拷贝,直接禁用RigidBody的拷贝构造和拷贝赋值,vector中存储智能指针即可:
class RigidBody { public: // 禁用拷贝 RigidBody(const RigidBody&) = delete; RigidBody& operator=(const RigidBody&) = delete; // 实现移动语义,支持vector内部移动元素 RigidBody(RigidBody&& other) noexcept { m_body = other.m_body; m_fixture = other.m_fixture; other.m_body = nullptr; // 置空原对象指针,避免析构重复销毁 other.m_fixture = nullptr; } RigidBody& operator=(RigidBody&& other) noexcept { if (this != &other) { // 先销毁自己当前持有的资源 if (m_body) Runtime::PhysicsWorld.DestroyBody(m_body); // 转移所有权 m_body = other.m_body; m_fixture = other.m_fixture; other.m_body = nullptr; other.m_fixture = nullptr; } return *this; } // 析构函数加判空 ~RigidBody() { if (m_body) Runtime::PhysicsWorld.DestroyBody(m_body); } // 其他原有逻辑... }; // 存储用智能指针避免拷贝 std::vector<std::unique_ptr<hv::RigidBody>> m_Bodies;
方案2:补全拷贝赋值运算符(如果确实需要值拷贝语义)
如果你确实需要复制RigidBody时生成完全独立的新物理体,就按照拷贝构造的逻辑实现拷贝赋值运算符:
RigidBody& RigidBody::operator=(const RigidBody& other) { if (this == &other) return *this; // 先销毁当前持有的物理资源 if (m_body) Runtime::PhysicsWorld.DestroyBody(m_body); // 复用拷贝构造逻辑创建新的物理体 m_fixture = other.m_fixture; b2BodyDef bodyDef; bodyDef.position = other.m_body->GetPosition(); bodyDef.type = other.m_body->GetType(); m_body = Runtime::PhysicsWorld.CreateBody(&bodyDef); b2FixtureDef fixtureDef; fixtureDef.shape = m_fixture->GetShape(); fixtureDef.density = m_fixture->GetDensity(); fixtureDef.friction = m_fixture->GetFriction(); fixtureDef.restitution = m_fixture->GetRestitution(); fixtureDef.restitutionThreshold = m_fixture->GetRestitutionThreshold(); m_fixture = m_body->CreateFixture(&fixtureDef); return *this; }
内容的提问来源于stack exchange,提问作者uselessguy
相关产品推荐
相关产品推荐

