为何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
相关产品推荐
相关产品推荐

