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

程序初始化后std::vector为何重置?ObjectTracker类异常排查

问题

我有一个用于跟踪对象创建的类,所有对象均为文件作用域的静态存储对象。类定义如下:

template <typename derived, std::size_t size = std::numeric_limits<std::size_t>::max()>
class ObjectTracker{
  private:
    ObjectTracker(ObjectTracker const &) = delete;
    ObjectTracker& operator=(ObjectTracker const &) = delete;

    static std::size_t total;
    static std::vector<derived*> objects;

    std::size_t id;

  protected:
    ObjectTracker(){
      if(total < size){
        objects.push_back(static_cast<derived*>(this));
        id = total++;
      }
      else throw std::length_error("Too many objects are being created in ObjectTracker subclass.");
    }

  public:
    static derived* getNth(std::size_t n) {return (n < getList().size()) ? getList()[n] : nullptr;}
    static std::size_t getCount() {return total;}
    static std::vector<derived*> const & getList() {return objects;}

    std::size_t getID() const {return id;}
};

template <typename derived, std::size_t size> std::size_t ObjectTracker<derived, size>::total;
template <typename derived, std::size_t size> std::vector<derived*> ObjectTracker<derived, size>::objects;

使用方式如下:

class Auton: public ObjectTracker<Auton>{
//Other stuff

  public:
    Auton(std::string name, std::function<void()> program, E_Reset_Types reset_type = E_Reset_Types::home){
    //saves vars
    };

出现的异常现象:

  • 对象创建期间检查getList(),能看到它已正确填充,元素顺序与创建顺序一致,大小也正确。
  • 但在main()中调用getList().size()返回0,getCount()仍返回正确数值,std::vector似乎被重置。
  • 行为不一致:4个派生自ObjectTracker的类中,2个正常工作,另外2个出现vector重置的情况。
  • 补充:代码在clang20环境运行正常,但在使用gnu20编译的VEX V5的PROS环境中出现问题。

分析与解决方案

核心原因:静态初始化顺序不确定性(Static Initialization Order Fiasco)

C++标准未规定跨翻译单元的文件作用域静态对象的动态初始化顺序,这是问题的根源:

  1. ObjectTracker的静态成员std::vector<derived*> objects属于动态初始化(vector默认构造不是常量表达式),而std::size_t total是POD类型,属于零初始化(程序启动阶段完成)。
  2. 对于出问题的派生类,其文件作用域静态对象的构造顺序早于objects的动态初始化:
    • 派生类对象构造时,会调用ObjectTracker的构造函数,向objects中添加指针并递增total。
    • 后续objects执行动态初始化(默认构造),直接覆盖之前的内容,导致vector被清空;但total是零初始化后被修改,不会再被重置,因此getCount()仍返回正确值。
  3. 部分类正常工作,是因为它们的静态对象构造顺序晚于objects的初始化,避免了覆盖问题。

修复方案:用函数内静态变量替代类静态成员

通过Meyers单例模式,将静态成员改为函数内的静态变量,保证第一次调用时才初始化,彻底消除初始化顺序问题:

template <typename derived, std::size_t size = std::numeric_limits<std::size_t>::max()>
class ObjectTracker{
  private:
    ObjectTracker(ObjectTracker const &) = delete;
    ObjectTracker& operator=(ObjectTracker const &) = delete;

    std::size_t id;

    // 改用函数内静态变量,确保初始化时机正确
    static std::size_t& total() {
        static std::size_t t = 0;
        return t;
    }
    static std::vector<derived*>& objects() {
        static std::vector<derived*> objs;
        return objs;
    }

  protected:
    ObjectTracker(){
      if(total() < size){
        objects().push_back(static_cast<derived*>(this));
        id = total()++;
      }
      else throw std::length_error("Too many objects are being created in ObjectTracker subclass.");
    }

  public:
    static derived* getNth(std::size_t n) {return (n < getList().size()) ? getList()[n] : nullptr;}
    static std::size_t getCount() {return total();}
    static std::vector<derived*> const & getList() {return objects();}

    std::size_t getID() const {return id;}
};
  • 函数内的静态变量会在第一次被调用时初始化,且初始化过程线程安全(C++11及以后标准)。
  • 移除原来的类静态成员定义,因为现在静态变量已在函数内部声明。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 11:15:44