目录

-原创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的核心特性: 独占所有权和自动释放. 以下是对代码中关键部分的详细说明:

  1. tfpro_ShowInt_Move()函数:

在调用时通过std::move将unique_ptr的所有权转移给线程, 传递后原unique_ptr变为null. 由于线程拥有了unique_ptr的所有权之后, 而pro_Init_UniqueVer()函数不在拥有, 所以pro_Init_UniqueVer()函数作用域结束后不会释放这块内存, 线程依然可以安全访问指针指向的值. 该方式在多线程场景中确保了数据的正确性和安全性.

  1. tfpro_ShowInt()函数

通过引用传递std::unique_ptr, 这意味着线程内访问的是pro_Init_UniqueVer()函数的资源. 由于pro_Init_UniqueVer()函数结束后会自动释放unique_ptr指向的内存, 导致线程中的引用变成悬空指针, 从而可能引发未定义行为. Debug版本通常会检测到这一问题并报错, 而Release版本可能由于优化原因, 不会报错, 但是会打印垃圾值.

  1. pro_Init_UniqueVer()函数

分别创建了两个std::unique_ptr实例, 并通过不同方式传递给线程. 通过对比两个线程的输出, 可以清楚地看到, 只有使用移动语义将unique_ptr转移到线程中, 才能确保数据被正确访问. 反之, 直接通过引用传递可能会出现内存访问错误或不可预知的结果.

[总结]

深入探讨了std::unique_ptr在现代C++中的作用域行为及其关键特性, 总结如下:

独占所有权: std::unique_ptr保证内存只有一个所有者, 通过移动语义(std::move)可以将所有权安全地转移给其他作用域或线程, 避免多个所有者导致的混乱.

自动释放: 当std::unique_ptr离开其作用域时, 它会自动释放所管理的内存, 无需手动调用delete, 从而降低内存泄漏的风险.

多线程注意事项: 在多线程环境中使用std::unique_ptr时, 若通过引用传递, 主线程作用域结束后内存可能被释放, 导致线程访问无效内存. 正确的做法是通过移动语义将所有权转移给线程, 确保线程独立管理内存的生命周期.