vector用emplace_back创建对象致map中对应指针属性数据丢失
从CSV文件读取数据创建Product对象存入std::vector,同时将对象指针以Product ID为键存入std::map,运行时出现std::bad_alloc错误,且打印map中对象属性时数据乱码。调试发现创建第二个Product对象时,map中前一个对象的属性数据被清除。尝试替换emplace_back为push_back、拆分循环等方法均未解决问题。
原因分析
vector内存重分配导致指针失效
std::vector在容量不足时,会自动重新分配更大的内存空间,并将原有元素拷贝到新内存中,原有元素的地址会失效。代码中每次调用products.emplace_back后,将&products.back()存入map,当vector触发内存重分配时,之前存入map的指针就变成了野指针,访问这些指针会导致数据乱码、内存访问错误,最终触发std::bad_alloc。拆分循环时的临时变量指针问题
拆分循环的写法中,for (auto product : products)是对vector中元素的值拷贝,每次循环的product是临时变量,取&product存入map后,循环结束临时变量销毁,map中的指针全部指向已释放的内存,同样会导致非法内存访问。
解决方案
方案1:提前预留vector容量,避免内存重分配
在创建products后,调用reserve方法提前分配足够的内存,确保后续emplace_back不会触发内存重分配,指针地址保持有效:
std::map<std::string, Product*> loadProductMap(std::string fileName) { std::vector<std::vector<std::string>> productMapFile = Reader::readCSV(fileName); std::vector<Product> products; // 提前预留足够容量,避免内存重分配 products.reserve(productMapFile.size()); std::map<std::string, Product*> productMap; for (size_t i = 0; i < productMapFile.size(); i++) { products.emplace_back(productMapFile.at(i).at(0), productMapFile.at(i).at(1), stod(productMapFile.at(i).at(2))); productMap.emplace(products.at(i).getProductID(), &products.at(i)); } return productMap; }
方案2:使用智能指针管理内存(推荐)
改用std::unique_ptr或std::shared_ptr存储对象,避免手动管理指针的生命周期,彻底消除野指针问题:
#include <memory> // 需包含头文件 std::map<std::string, std::unique_ptr<Product>> loadProductMap(std::string fileName) { std::vector<std::vector<std::string>> productMapFile = Reader::readCSV(fileName); std::map<std::string, std::unique_ptr<Product>> productMap; for (auto& row : productMapFile) { // 直接在map中创建智能指针管理的Product对象 productMap.emplace(row[0], std::make_unique<Product>(row[0], row[1], stod(row[2]))); } return productMap; } // main函数中遍历方式调整 int main() { auto productMap = loadProductMap("product_list.csv"); for (auto& i : productMap) { std::cout << i.second->getProductID() << " " << i.second->getProductName() << " " << i.second->getProductPrice() << std::endl; } return 0; }
方案3:map直接存储Product对象,避免指针
如果不需要共享对象,直接让map存储Product对象副本,代码更简洁安全:
std::map<std::string, Product> loadProductMap(std::string fileName) { std::vector<std::vector<std::string>> productMapFile = Reader::readCSV(fileName); std::map<std::string, Product> productMap; for (auto& row : productMapFile) { productMap.emplace(row[0], Product(row[0], row[1], stod(row[2]))); } return productMap; } // main函数中遍历方式调整 int main() { auto productMap = loadProductMap("product_list.csv"); for (auto& i : productMap) { std::cout << i.second.getProductID() << " " << i.second.getProductName() << " " << i.second.getProductPrice() << std::endl; } return 0; }
补充说明
- 方案1仅适用于vector后续不会再触发内存重分配的场景,若后续仍有添加元素操作,仍可能出现指针失效问题;
- 方案2和方案3从根本上避免了野指针问题,是更健壮的实现方式,其中方案2适合需要共享对象的场景,方案3适合简单存储的场景。
内容的提问来源于stack exchange,提问作者Chris Allison

