C++中double free or corruption(out)问题的原因及修复咨询
分析并修复double free or corruption (out)错误(进程退出码134)
错误根源分析
你这里的问题很典型——你把栈上的局部对象地址交给了std::unique_ptr管理,而且还重复添加同一个地址到Data容器里。
仔细拆解你的代码问题点:
AccountsStruct accountsStruct;是LoadTable函数内的栈局部变量,整个循环过程中它的内存地址完全固定,每次循环只是覆盖修改它的成员值而已。- 调用
Data.emplace_back(&accountsStruct)时,unique_ptr会接管这个栈对象的地址,但栈对象的生命周期由函数作用域管控,根本不属于堆内存范畴。当循环结束后,accountsStruct会被自动销毁,内存被系统回收。 - 更致命的是,你每次循环都往
Data里塞同一个地址,导致Data里的所有unique_ptr都指向同一块已经被标记为回收的栈内存。当Data(或者其中的unique_ptr)触发析构时,每个unique_ptr都会尝试释放这块内存,直接触发double free错误;另外,栈内存本来就不该由unique_ptr来释放,这种越界操作本身就会引发内存 corruption。
修复方案
你需要为每个账户创建独立的堆内存对象,让unique_ptr管理合法的堆内存。这里提供两种简洁的修复方式:
方式1:用std::make_unique直接构造堆对象(推荐)
如果你的项目支持C++14及以上,优先用std::make_unique,它比直接new更安全,能避免潜在的内存泄漏风险:
#include "Accounts.h" using namespace Vibranium; void Accounts::LoadTable(RowResult &res) { for (Row row : res.fetchAll()){ // 直接在堆上构造AccountsStruct,make_unique会自动包装为unique_ptr Data.emplace_back(std::make_unique<AccountsStruct>( row[0].get<int>(), row[1].get<std::string>(), row[2].get<std::string>(), row[4].get<int>(), row[5].get<std::string>(), row[6].get<std::string>(), row[7].get<std::string>(), row[8].get<int>(), row[9].get<int>(), row[10].get<int>(), row[11].get<double>(), row[12].get<std::string>(), row[13].get<std::string>(), row[15].get<int>(), row[16].get<int>(), row[17].get<int>() )); } std::cout << "SIZE: " << Data.size() << std::endl; }
注意:要确保参数顺序和AccountsStruct的构造函数参数(或成员变量顺序)完全匹配,如果结构体有默认构造函数,也可以用成员初始化列表的方式来写,可读性更强。
方式2:保留原有赋值逻辑,复制栈对象到堆
如果觉得直接构造参数太长可读性差,可以保留你原来的赋值逻辑,每次循环都创建新的堆对象:
#include "Accounts.h" using namespace Vibranium; void Accounts::LoadTable(RowResult &res) { for (Row row : res.fetchAll()){ AccountsStruct accountsStruct; accountsStruct.id = row[0].get<int>(); accountsStruct.email = row[1].get<std::string>(); accountsStruct.warTag = row[2].get<std::string>(); accountsStruct.state = row[4].get<int>(); accountsStruct.name = row[5].get<std::string>(); accountsStruct.lastname = row[6].get<std::string>(); accountsStruct.country = row[7].get<std::string>(); accountsStruct.dob_month = row[8].get<int>(); accountsStruct.dob_day = row[9].get<int>(); accountsStruct.dob_year = row[10].get<int>(); accountsStruct.balance = row[11].get<double>(); accountsStruct.created_at = row[12].get<std::string>(); accountsStruct.updated_at = row[13].get<std::string>(); accountsStruct.account_role = row[15].get<int>(); accountsStruct.rank = row[16].get<int>(); accountsStruct.playerRole = row[17].get<int>(); // 把栈对象复制到堆内存,交给unique_ptr管理 Data.emplace_back(std::make_unique<AccountsStruct>(accountsStruct)); } std::cout << "SIZE: " << Data.size() << std::endl; }
关键知识点回顾
std::unique_ptr的核心设计目标是管理堆内存,它会在生命周期结束时自动调用delete释放内存。如果给它传递栈对象的地址,delete栈内存是完全的未定义行为,必然会触发内存错误。- 栈局部变量的生命周期仅限于定义它的作用域(这里就是
LoadTable函数内部),一旦离开作用域,内存就会被操作系统回收,后续再访问该地址都是非法的。
内容的提问来源于stack exchange,提问作者Venelin
相关产品推荐
相关产品推荐

