You need to enable JavaScript to run this app. C++如何同时删除基类Model和派生类Bullet的shared_ptr向量中同一对象
优惠活动
大模型
产品
解决方案
定价
更多

C++如何同时删除基类Model和派生类Bullet的shared_ptr向量中同一对象

双向量shared_ptr存储派生类实例的删除方案

首先明确你的疑问:

我需要将对象从两个向量中都删除才能让对象析构销毁,对吗?
是的。std::shared_ptr基于引用计数管理对象生命周期,你的两个向量各持有一个该实例的强引用,只有两个引用都被移除、引用计数归0时,对象才会自动触发析构。你之前尝试delete this崩溃的原因也在此:手动释放对象后,持有该指针的shared_ptr析构时会触发二次释放,直接导致内存错误。


推荐解决方案

以下方案按实现成本从低到高、适配不同规模的项目需求:

方案1:批量匹配删除(中小规模项目首选,实现成本最低)

不需要为每个实例分配ID,直接利用指针地址做匹配批量删除即可,对于绝大多数2D/小型3D游戏来说,向量连续存储的缓存命中率远高于哈希查找,实际性能完全够用。
实现步骤:

  • 遍历bullets向量筛选出生命周期结束的子弹,收集这些子弹的原始指针地址到临时集合,同时删除bullets中的对应条目
  • 遍历models向量,删除地址匹配的条目
    示例代码:
// 收集待删除子弹的原始指针地址,同时清理bullets向量
std::unordered_set<const Model*> expired_bullets;
std::erase_if(bullets, [&](const std::shared_ptr<Bullet>& bullet) {
    if (bullet->check_lifecycle_end()) {
        expired_bullets.insert(bullet.get());
        return true;
    }
    return false;
});

// 同步清理models向量
std::erase_if(models, [&](const std::shared_ptr<Model>& model) {
    return expired_bullets.contains(model.get());
});

方案2:调整强引用持有逻辑(无遍历开销,适合中等规模项目)

调整所有权逻辑,仅让models向量持有所有实例的强引用,bullets向量改为存储std::weak_ptr<Bullet>弱引用,不持有所有权:

  • 新增子弹时,先创建shared_ptr<Bullet>存入models,再构造对应weak_ptr<Bullet>存入bullets
  • 执行子弹专属逻辑时,先调用weak_ptr::lock()获取强引用,判断实例是否有效再处理
  • 生命周期结束时,直接从models中删除对应实例,引用计数自动归0触发析构,bullets中的对应弱引用会自动标记为失效,下次遍历bullets时清理掉expired()的条目即可
    该方案删除操作不需要遍历整个models向量,仅需要在每次遍历bullets时顺带清理失效条目,额外开销极低。

方案3:对象池+句柄方案(大型游戏首选,性能最优)

如果项目对象量级过万,可以改用ECS架构中常用的对象池+句柄方案:

  • 所有Model实例存储在结构数组(AoS)或者数组结构(SoA)的对象池中,用包含索引+版本号的32/64位句柄唯一标识实例
  • bullets和models都仅存储句柄,不持有对象所有权
  • 生命周期结束时仅需要标记对象池中的对应实例为空闲,下次分配时复用即可,不需要遍历删除,性能开销几乎为0

内容的提问来源于stack exchange,提问作者Aubrey Champagne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.22 05:36:05