一般来说,使用 raw 指针时才容易出现野指针问题。使用智能指针(如 shared_ptrunique_ptr)管理内存时,此类问题相对不易发生,因此本文不对智能指针方案展开阐述。

在展开叙述之前,我们先看几个来自成熟引擎的实现原理与方案,从中汲取思路。

来自引擎的启示

Unity 引擎的做法:Unity 中的 GameObject 等内置对象在被 Destroy 后,所有引用变量都会变为 null。Unity 实现这一点的方式是重载 ==!= 等操作符,在使用前先验证对象是否有效。这给我们的启示是:指针不必直接暴露,可以通过封装一个 handle 来间接指代真实指针,从而实现可追踪、可记录、可有效性验证。同样的思路可以应用到 C++ 侧的指针管理上——在使用前确认指针是否仍然有效。

PhysX 物理引擎的做法:PhysX 的内存分配与释放调用点会记录文件名、函数名、行号等 RTTI 信息,用于日志输出。这使得内存问题发生时可以快速定位到具体的分配或释放位置。

野指针的根本原因

野指针的根本原因是:已析构的指针仍在被使用。生命周期管理逻辑混乱、异常处理不当、时序问题、多线程并发访问等,都是导致此类问题的常见来源。

BUG 查找方法

基于上述分析,野指针 BUG 的查找可以转化为:以内存清理点的调用逻辑正确性作为切入点。具体步骤如下:

  1. 追踪到内存清理的调用点——通过记录 RTTI 信息(文件名、函数名、行号),可以定位到是哪一行代码触发了清理。
  2. 找到清理调用点后,阅读代码逻辑,验证该清理操作在此时发生是否符合预期的生命周期管理逻辑。

通过这一思路,可以将无从下手的野指针问题转化为有据可查的逻辑正确性分析。