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

在C++中能否将迭代器存储在结构体中?若不可行,原因是什么?

问题:能否将Boost Property Tree的迭代器存储在结构体中?

我需要从多个JSON文件读取数据,打算在初始化完成后再执行实际读取操作。出于特定原因,我认为把每个JSON文件首个元素对应的迭代器存储起来会很有用,这样后续就能遍历每个JSON文件并读取第i个元素。

但遇到了问题:如果创建迭代器后立即用它解析数据,一切正常;但把迭代器存储到结构体中,后续再使用时会出现读访问违例。以下是相关代码片段:

首先是存储单个JSON文件信息的结构体声明:

typedef struct _struct {
    ...
    boost::property_tree::ptree json_struct;
    boost::property_tree::ptree::iterator it;
    ...
} json_handler;

然后是存储多个handler的容器:

std::map<std::string, json_handler> mjh;

这段代码可以正常工作:

json_handler cms;
boost::property_tree::read_json(full_path, cms.json_struct);
boost::property_tree::ptree &data = cms.json_struct.get_child("frames");
// 如果在此处直接遍历JSON内容,一切正常
for (boost::property_tree::ptree::iterator element = data.begin(); element != data.end(); element++)
    double time = element->second.get<double>("ST");
// 将当前json_handler插入到map中
mjh.insert(std::pair<std::string,json_handler>(name, cms));

但这段后续使用数据的代码无法正常工作:

// 后续使用数据的代码
std::map<std::string, json_handler>::iterator it;
for (it = mjh.begin(); it != mjh.end(); it++) {
    double _st = it->second.it->second.get<double>("ST");
    std::cout << "ST: " << std::to_string(_st ) << std::endl;
    ...
}

请问:能否将迭代器存储在结构体中?若不可行,原因是什么?


回答

当然可以存储迭代器,但你的问题出在迭代器失效上,具体原因如下:

当你执行mjh.insert(std::pair<std::string,json_handler>(name, cms))时,你把cms这个json_handler对象拷贝到了std::map中。而你之前存在cms.it里的迭代器,是指向cms.json_struct.get_child("frames")这个子树的内部数据的——这个子树属于原来的cms对象。

一旦拷贝完成,map里的json_handler是一个完全独立的副本:它的json_struct拥有自己的内存空间,和原来的cms没有任何关系。而你存储的迭代器依然指向原来cms对象的frames子树内存。如果原来的cms是局部变量,之后会被销毁,那这片内存就变成了无效内存;就算cms还存在,你用这个迭代器去访问map里的副本对象的json_struct,本质上是在访问错误的内存区域,自然会触发读访问违例。

Boost Property Tree的迭代器和STL容器迭代器的特性类似:它们绑定到特定的容器实例,当容器被拷贝、移动或者销毁时,原来的迭代器就会失效,无法再用于访问新的容器实例。

解决办法

有几种可行的方案来修复这个问题:

1. 使用智能指针存储json_handler,避免拷贝

把std::map的 value 类型改成智能指针,这样就不会拷贝整个json_handler对象,只会传递指针,迭代器指向的还是同一个ptree实例:

// 修改容器类型
std::map<std::string, std::unique_ptr<json_handler>> mjh;

// 创建并初始化handler
auto cms = std::make_unique<json_handler>();
boost::property_tree::read_json(full_path, cms->json_struct);
boost::property_tree::ptree &data = cms->json_struct.get_child("frames");
cms->it = data.begin();

// 插入到map中(移动语义,避免拷贝)
mjh.emplace(name, std::move(cms));

// 后续使用时
for (auto &entry : mjh) {
    double _st = entry.second->it->second.get<double>("ST");
    std::cout << "ST: " << std::to_string(_st) << std::endl;
}

2. 不存储迭代器,每次需要时重新获取

如果你的场景允许,也可以不存储迭代器,每次需要访问时重新获取frames子树的迭代器:

// 修改结构体,去掉存储的迭代器
typedef struct _struct {
    ...
    boost::property_tree::ptree json_struct;
    ...
} json_handler;

// 后续使用时
for (auto &entry : mjh) {
    auto &frames = entry.second.json_struct.get_child("frames");
    if (!frames.empty()) {
        double _st = frames.begin()->second.get<double>("ST");
        std::cout << "ST: " << std::to_string(_st) << std::endl;
    }
}

这种方法虽然每次都要调用get_child和begin(),但胜在简单安全,不会有迭代器失效的问题。

3. 存储frames子树的副本

如果整个JSON里你只关心frames部分,也可以直接在结构体里存储frames子树的副本,而不是整个JSON结构,这样迭代器指向的就是结构体内部的子树,拷贝后也依然有效:

typedef struct _struct {
    ...
    boost::property_tree::ptree frames;
    boost::property_tree::ptree::iterator it;
    ...
} json_handler;

// 初始化时
json_handler cms;
boost::property_tree::ptree temp_json;
boost::property_tree::read_json(full_path, temp_json);
cms.frames = temp_json.get_child("frames");
cms.it = cms.frames.begin();
mjh.insert(std::pair<std::string,json_handler>(name, cms));

这样拷贝json_handler时,frames和迭代器都会被正确拷贝,迭代器指向的是副本里的frames子树,访问就不会出错了。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 01:42:48