-原创Modern-C现代C的关键性概念-深度验证智能指针stdunique_ptr的作用域,-轻松了解它的内部细节.
[原创](Modern C++)现代C++的关键性概念: 深度验证智能指针std::unique_ptr的作用域, 轻松了解它的内部细节.
[作者]
常用网名: 猪头三
出生日期: 1981.XX.XX
企鹅交流: 643439947
个人网站:
编程生涯: 2001年~至今[共24年]
职业生涯: 22年
开发语言: C/C++、80x86ASM、Object Pascal、Objective-C、C#、R、Python、PHP、Perl、
开发工具: Visual Studio、Delphi、XCode、C++ Builder、Eclipse
技能种类: 逆向 驱动 磁盘 文件 大数据分析
涉及领域: Windows应用软件安全/Windows系统内核安全/Windows系统磁盘数据安全/macOS应用软件安全
项目经历: 股票模型量化/磁盘性能优化/文件系统数据恢复/文件信息采集/敏感文件监测跟踪/网络安全检测
专注研究: 机器学习、股票模型量化、金融分析
[序言]
在现代C++编程中, 内存管理一直是需要特别关注的核心问题之一. 传统的裸指针容易引发内存泄漏, 野指针或重复释放等问题, 而智能指针的引入则极大地简化了动态内存的管理. 在众多智能指针中, std::unique_ptr以其独占所有权的特性脱颖而出. 它不仅能够自动管理内存的生命周期, 避免手动释放的麻烦, 还通过编译期的约束确保了内存安全. 深入了解std::unique_ptr在作用域中的行为, 剖析其内部机制, 有助于提升代码的安全性和健壮性, 尤其是在多线程环境下, 让多线程运行更加安全.
[代码演示]
void tfpro_ShowInt_Move(std::unique_ptr<int> param_ptr_Int)
{
std::this_thread::sleep_for(std::chrono::seconds(5));
if (param_ptr_Int)
{
// 正确打印: 符合预期
std::wcout << L"Thread: unique_ptr Value is moved " << *param_ptr_Int << std::endl;
}
else
{
std::wcout << L"Thread: ptr is null" << std::endl;
}
}// End tfpro_ShowInt_Move()
void tfpro_ShowInt(std::unique_ptr<int>& param_ptr_Int)
{
// 可以等待3秒, 让pro_Init_UniqueVer()运行结束, 结束之后std::unique_ptr<int>会自动释放内存
std::this_thread::sleep_for(std::chrono::seconds(5));
if (param_ptr_Int)
{
// 这里可能报错: pro_Init_UniqueVer()作用域结束后param_ptr_Int已被释放
// 注意: 在Release模式下可能打印垃圾值, 但这属于未定义行为
std::wcout << L"Thread: unique_ptr Value is " << *param_ptr_Int << std::endl;
}
else
{
std::wcout << L"Thread: ptr is null" << std::endl;
}
}// End tfpro_ShowInt()
void pro_Init_UniqueVer()
{
// 初始化int数值为10
std::unique_ptr<int> pointer_Int = std::make_unique<int>(10);
std::unique_ptr<int> pointer_Int_Move = std::make_unique<int>(10);
// 创建线程, 把pointer_Int引用入线程作用域内部, 然后分离线程,使其独立运行
// 结果输出错误
// Thread: unique_ptr Value is 24983816
std::thread(tfpro_ShowInt, std::ref(pointer_Int)).detach();
// 创建线程, 把pointer_Int_Move移动入线程作用域内部, 然后分离线程,使其独立运行
// 结果输出正确
// Thread: unique_ptr Value is moved 10
std::thread(tfpro_ShowInt_Move, std::move(pointer_Int_Move)).detach();
// 注意:函数pro_Init_UniqueVer()在这里结束, pointer_Int将被释放, 而由于pointer_Int_Move使用了移动, 所以直接无效.
}// End pro_Init_UniqueVer()
int main()
{
_setmode(_fileno(stdout), _O_WTEXT);
// 调用初始化函数, 验证std::unique_ptr特性, 比如"独占所有权", "自动释放"的概念
pro_Init_UniqueVer();
std::cin.get();
return 0;
}
[代码说明]
上述代码通过多线程的方式展示了std::unique_ptr的核心特性: 独占所有权和自动释放. 以下是对代码中关键部分的详细说明:
- tfpro_ShowInt_Move()函数:
在调用时通过std::move将unique_ptr的所有权转移给线程, 传递后原unique_ptr变为null. 由于线程拥有了unique_ptr的所有权之后, 而pro_Init_UniqueVer()函数不在拥有, 所以pro_Init_UniqueVer()函数作用域结束后不会释放这块内存, 线程依然可以安全访问指针指向的值. 该方式在多线程场景中确保了数据的正确性和安全性.
- tfpro_ShowInt()函数
通过引用传递std::unique_ptr, 这意味着线程内访问的是pro_Init_UniqueVer()函数的资源. 由于pro_Init_UniqueVer()函数结束后会自动释放unique_ptr指向的内存, 导致线程中的引用变成悬空指针, 从而可能引发未定义行为. Debug版本通常会检测到这一问题并报错, 而Release版本可能由于优化原因, 不会报错, 但是会打印垃圾值.
- pro_Init_UniqueVer()函数
分别创建了两个std::unique_ptr实例, 并通过不同方式传递给线程. 通过对比两个线程的输出, 可以清楚地看到, 只有使用移动语义将unique_ptr转移到线程中, 才能确保数据被正确访问. 反之, 直接通过引用传递可能会出现内存访问错误或不可预知的结果.
[总结]
深入探讨了std::unique_ptr在现代C++中的作用域行为及其关键特性, 总结如下:
独占所有权: std::unique_ptr保证内存只有一个所有者, 通过移动语义(std::move)可以将所有权安全地转移给其他作用域或线程, 避免多个所有者导致的混乱.
自动释放: 当std::unique_ptr离开其作用域时, 它会自动释放所管理的内存, 无需手动调用delete, 从而降低内存泄漏的风险.
多线程注意事项: 在多线程环境中使用std::unique_ptr时, 若通过引用传递, 主线程作用域结束后内存可能被释放, 导致线程访问无效内存. 正确的做法是通过移动语义将所有权转移给线程, 确保线程独立管理内存的生命周期.