You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++悬垂指针问题:游戏引擎对象销毁后避免崩溃的替代方案

游戏引擎对象销毁后悬空指针的解决方案

问题描述

开发游戏引擎时遇到元素销毁相关问题:多个相机对象跟踪注视目标(GameObject或Actor),目标销毁后,相机代码因访问已释放对象崩溃。简化代码示例如下:

int* refToMyInt = nullptr; // 需要支持空指针状态
int* refToMyInt2 = nullptr;

int* myInt = new int(2);
refToMyInt = myInt;
refToMyInt2 = refToMyInt;

delete myInt;

if(refToMyInt == nullptr && refToMyInt2 == nullptr)
  std::cout << "myInt Deleted!" << std::endl; // 永远不会执行

核心痛点:delete不会自动将所有引用指针置为nullptr,手动维护数十个引用不现实;weak_ptr每次访问需调用lock(),操作繁琐,求替代方案。


可行解决方案

方案1:侵入式存活标记+延迟销毁

给所有可跟踪对象(如GameObject/Actor)添加存活状态标记,销毁时仅标记为"已死亡",延迟到合适时机(如帧末尾)再释放内存,确保当前帧内所有引用可安全检查状态。

示例代码:

class TrackableObject {
public:
    bool isAlive = true;

    // 标记为死亡,不立即释放
    void MarkForDestroy() {
        isAlive = false;
    }
};

// 全局延迟销毁队列
std::vector<TrackableObject*> destroyQueue;

// 使用示例
TrackableObject* target = new TrackableObject();
TrackableObject* cameraRef = target;

// 销毁时标记并加入队列
target->MarkForDestroy();
destroyQueue.push_back(target);

// 帧末统一释放
for (auto obj : destroyQueue) {
    delete obj;
}
destroyQueue.clear();

// 访问前检查
if (!cameraRef->isAlive) {
    std::cout << "目标已销毁,跳过访问" << std::endl;
}

方案2:句柄+对象池模式

不暴露原始指针,用整数句柄间接引用对象,通过对象池维护映射关系与存活状态,销毁时仅更新池内标记,无需修改所有引用。

示例代码:

#include <unordered_map>

class TrackableObject {};

class ObjectPool {
private:
    std::unordered_map<int, TrackableObject*> objects;
    std::unordered_map<int, bool> isAlive;
    int nextHandle = 0;

public:
    int CreateObject() {
        int handle = nextHandle++;
        objects[handle] = new TrackableObject();
        isAlive[handle] = true;
        return handle;
    }

    void DestroyObject(int handle) {
        if (isAlive[handle]) {
            delete objects[handle];
            isAlive[handle] = false;
        }
    }

    TrackableObject* GetObject(int handle) {
        return (isAlive.count(handle) && isAlive[handle]) ? objects[handle] : nullptr;
    }
};

// 使用示例
ObjectPool pool;
int targetHandle = pool.CreateObject();
int cameraHandle = targetHandle;

pool.DestroyObject(targetHandle);

TrackableObject* target = pool.GetObject(cameraHandle);
if (target == nullptr) {
    std::cout << "目标已销毁,跳过访问" << std::endl;
}

方案3:封装简化版智能指针

基于weak_ptr封装自定义智能指针,自动处理空检查,隐藏lock()操作细节,用法接近原始指针。

示例代码:

#include <memory>

template<typename T>
class SafePtr {
private:
    std::weak_ptr<T> weakPtr;

public:
    SafePtr(std::shared_ptr<T> ptr) : weakPtr(ptr) {}

    // 重载->运算符,自动lock并返回指针
    T* operator->() {
        return weakPtr.lock().get();
    }

    // 显式检查有效性
    bool IsValid() const {
        return !weakPtr.expired();
    }
};

// 使用示例
auto myInt = std::make_shared<int>(2);
SafePtr<int> refToMyInt(myInt);
SafePtr<int> refToMyInt2(myInt);

myInt.reset(); // 销毁对象

if (!refToMyInt.IsValid() && !refToMyInt2.IsValid()) {
    std::cout << "myInt Deleted!" << std::endl;
}

// 安全访问
if (refToMyInt.IsValid()) {
    // 操作*refToMyInt或refToMyInt->xxx
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 13:17:37