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

为何vector中Timer._d的指针地址在两种访问方式下不一致?

问题原因分析

这是std::vector的扩容机制导致的:

  • std::vector初始化时会分配一块初始容量的内存,当调用emplace_back添加元素且当前容量不足时,vector会重新分配更大的内存空间,将原有元素拷贝或移动到新内存后释放旧内存。
  • 第一个Timer对象在第一次emplace_back时存入vector的初始内存,setTime中记录的_d地址是旧内存地址;第二次emplace_back触发扩容,第一个Timer被移动到新内存位置,后续遍历vector时获取的_d地址是新内存地址,和之前记录的旧地址不一致。
  • 第二个Timer在扩容后的内存中创建,后续未再触发扩容,因此_d地址在两次访问中保持一致。
解决方案

针对你需要稳定Data指针传给外部回调的场景,可选择以下方案:

方案1:提前预留vector容量

若能预估Timer的存储数量,提前用reserve分配足够内存,避免后续扩容:

int main() {
  std::vector<Timer> vec;
  vec.reserve(2); // 按需修改预留的元素数量

  vec.emplace_back(Timer());
  vec.back().setTime(1);

  vec.emplace_back(Timer());
  vec.back().setTime(2);

  // 后续遍历操作...
}

此方式下vector不会触发扩容,所有Timer对象地址始终不变,_d指针保持有效。

方案2:用vector存储智能指针

让vector存储std::unique_ptr<Timer>(或std::shared_ptr),vector元素为堆上Timer对象的指针。即使vector扩容,仅指针自身位置变化,堆上Timer对象地址不变,_d指针也稳定:

#include <memory>

int main() {
  std::vector<std::unique_ptr<Timer>> vec;

  vec.emplace_back(std::make_unique<Timer>());
  vec.back()->setTime(1, timer_complete);

  vec.emplace_back(std::make_unique<Timer>());
  vec.back()->setTime(2, timer_complete);

  for(auto &timer_ptr : vec) {
    timer_ptr->setDuration(3);
  }
}

适合不确定元素数量的场景,无需提前预留容量。

方案3:将Data存储在堆上

修改Timer类,把Data成员改为智能指针,让Data存放在堆上。即使Timer被vector扩容移动,堆上Data的地址也不会变化:

struct Data {
  int duration = 0;
};

class Timer : public Countimer {
  private:
    std::unique_ptr<Data> _d = std::make_unique<Data>();
  public:
    Timer () {}
    void setTime(int duration, timer_callback cb) {
      _d->duration = duration;
      setCounter(duration, cb, (void *)_d.get());
    }

    const Data* getDataPtr() {
     return _d.get();
    }

    void setDuration(int duration) {
      _d->duration = duration;
    }
};

无需修改vector的使用方式,仅调整Timer类内部实现,适合保持现有容器使用习惯的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 21:37:47