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

向std::vector插入元素后指针失效触发访问违例异常,求问题原因

问题:std::vector插入元素后指针失效触发访问违例

我在向std::vector插入新元素时遇到问题,指针无法正常工作。以下是我的代码:

Cell.h

#pragma once
#include <string>
#include <vector>

class Cell
{
public:
    Cell(std::string s) : s(s) {}

    std::string s;
    void test() {}
    std::vector<Cell*> refs;
};

main.cpp

#include <iostream>
#include <vector>

#include "Cell.h"

int main(int argc, char* argv[])
{
    std::vector<Cell> cells = {Cell("1"), Cell("2"), Cell("3")};
    cells[2].refs.push_back(&cells[0]);

    std::string val = cells[2].refs[0]->s;

    cells.insert(cells.begin() + 1, Cell("4"));

    val = cells[3].refs[0]->s; // 触发访问违例异常
    
    return 0;
}

插入Cell("4")后,指针失效并触发访问违例异常,请问哪里出错了?


解答

问题根源

std::vector的底层是连续内存存储,当插入元素导致当前容量不足时,它会重新分配一块更大的内存空间,把原有的所有元素复制/移动到新内存,然后释放旧内存。这就导致你之前存储的&cells[0]指针,指向的是已经被释放的旧内存区域,变成了野指针,后续访问自然会触发内存访问异常。

在你的代码里,插入Cell("4")时,原vector的容量可能刚好等于元素数量(初始3个元素,容量大概率是3),插入第4个元素时触发扩容,原有元素的地址全部失效,cells[2].refs[0]保存的旧地址已经无效,访问时就会报错。

解决方案

针对这个问题,有几种可行的解决思路:

1. 使用智能指针存储对象(推荐)

把std::vector<Cell>改成std::vector<std::unique_ptr<Cell>>或者std::vector<std::shared_ptr<Cell>>,让对象存储在堆内存中,vector里只保存指向堆对象的指针。这样即使vector扩容重新分配内存,堆对象的地址不会改变,指针依然有效。

修改后的代码示例:

#include <iostream>
#include <vector>
#include <memory>

#include "Cell.h"

int main(int argc, char* argv[])
{
    std::vector<std::unique_ptr<Cell>> cells;
    cells.emplace_back(std::make_unique<Cell>("1"));
    cells.emplace_back(std::make_unique<Cell>("2"));
    cells.emplace_back(std::make_unique<Cell>("3"));

    cells[2]->refs.push_back(cells[0].get());

    std::string val = cells[2]->refs[0]->s;

    cells.insert(cells.begin() + 1, std::make_unique<Cell>("4"));

    val = cells[3]->refs[0]->s; // 正常访问
    
    return 0;
}

2. 提前预留足够容量

如果确定后续插入的元素数量,可以提前用reserve()方法为vector分配足够的内存空间,避免扩容。这样插入元素时不会触发内存重分配,原有元素的地址保持不变。

修改后的main.cpp示例:

int main(int argc, char* argv[])
{
    std::vector<Cell> cells = {Cell("1"), Cell("2"), Cell("3")};
    cells.reserve(4); // 提前预留至少4个元素的容量

    cells[2].refs.push_back(&cells[0]);

    std::string val = cells[2].refs[0]->s;

    cells.insert(cells.begin() + 1, Cell("4"));

    val = cells[3].refs[0]->s; // 正常访问
    
    return 0;
}

注意:这种方法只适用于能提前确定元素数量的场景,如果后续插入的元素超过预留容量,还是会触发扩容,指针依然会失效。

3. 改用非连续内存容器

比如std::list,它的底层是双向链表,插入元素时不会移动其他元素的位置,因此指向元素的指针/迭代器在插入操作后不会失效(除了被删除的元素)。但std::list不支持随机访问,性能上和std::vector有差异,需要根据业务场景选择。


内容的提问来源于stack exchange,提问作者Ростислав Романець

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 08:10:22