C++中vector用emplace_back添加元素后地址变化的解决方法
核心问题原因
emplace_back的误用:你当前的写法
m_elements.emplace_back(element(x11,x12,x21,x22,x31,x32))是先构造一个临时element对象,再将这个临时对象**移动(或复制)**到vector中。临时对象的内部成员(比如m_nodes)和vector中最终存储的element的成员内存地址自然不同——因为临时对象的内存是栈上的,而vector会把它移到自己的堆内存空间里。地址打印的错误:你在
elementList构造函数里打印的是initialNode的地址,而initialNode = m_elements.at(0).nodes().at(0)是把容器内的Node拷贝到了一个局部变量里,这个局部变量的地址当然和容器内m_nodes[0]的地址完全无关。vector扩容的潜在影响:即使正确使用emplace_back,如果vector的容量不足,添加元素时会触发内存重新分配,导致所有已存储的element对象被移动到新的内存区域,它们的内部成员地址也会随之改变。
解决方案
1. 正确使用emplace_back,避免临时对象
emplace_back的设计目的就是直接在vector的内存空间中构造对象,不需要先创建临时对象。把代码改成:
m_elements.emplace_back(x11, x12, x21, x22, x31, x32);
这样就会跳过临时对象的创建,直接在vector的内存里构造element,从根源上避免一次移动/拷贝操作。
2. 提前预分配vector内存,防止扩容
如果你能提前知道要加载的element数量,在构造elementList时先调用reserve()预分配足够的内存:
elementList::elementList(std::string filename) { // 先解析出element的总数count size_t count = ...; // 从文件中获取元素数量 m_elements.reserve(count); // 再逐个构造element m_elements.emplace_back(x11, x12, x21, x22, x31, x32); // ... 其他元素 }
reserve()会让vector提前分配足够的内存空间,后续添加元素时不会触发重新分配,element对象及其内部成员的地址就会保持不变。
3. 修正地址打印的方式
要打印容器内element的内部节点地址,不要拷贝到局部变量,直接取原对象的地址:
std::cout << &m_elements.at(0).nodes().at(0);
这样得到的地址才会和element构造函数中打印的&m_nodes[0]一致(在没有扩容的前提下)。
4. 备选方案:使用不移动的容器
如果你的场景无法提前确定元素数量,或者需要绝对稳定的内存地址,可以考虑使用std::list代替std::vector——list的元素存储在独立的节点中,添加元素不会导致已有元素的内存地址变化。但list的随机访问性能不如vector,需要根据你的业务场景权衡。
内容的提问来源于stack exchange,提问作者3RaccoonsInATrenchcoat

