C++ Rall机制详解

RAII 是 resource acquisition is initialization 的缩写,意为“资源获取即初始化”。它是 C++ 之父 Bjarne Stroustrup 提出的设计理念,其核心是把资源和对象的生命周期绑定,对象创建获取资源,对象销毁释放资源。在 RAII 的指导下,C++ 把底层的资源管理问题提升到了对象生命周期管理的更高层次。


那么到底什么是 RALL 机制?


使用 C++ 时,最让人头疼的便是内存管理,但却又正是对内存高度的可操作性给了 C++ 程序猿极大的自由与装逼资本。

当我们 new 出一块内存空间,在使用完之后,如果不使用 delete 来释放这块资源则将导致内存泄露,这在中大型项目中是极具破坏性的。但是人无完人,我们并不能保证每次都记得释放无法再次获取到不再使用的内存,下面我给出一个例子,大家看看忘记释放资源而造成内存泄露是多么恐怖!!

#include <iostream>
#include <memory>


int main()
{
	for (int i = 1; i <= 10000000; i++)
	{
		int32_t *ptr = new int32_t[3];
		ptr[0] = 1;
		ptr[1] = 2;
		ptr[2] = 3;
		//delete ptr;     //假设忘记了释放内存
	}
	system("pause");
	return 0;
}

运行程序,打开资源管理器,可以这么简单的一个程序竟然就已经占用了536.7MB的内存,所以大家应该祈祷千万不要犯这么低级的错误!



可是祈祷真的有用吗?作为一名程序猿,最好不要把自己的命运交给上帝,而应该扮演上帝的角色。

有没有什么方法能够保证资源的自动释放呢?就像JAVA一样,但是却又不失C++程序猿的面子。

这个时候我们想到对象的析构是自动完成的,那么可不可以利用这个机制呢?答案很明确,可以。我们需要做的便是将资源托管给某个对象,或者说这个对象是资源的代理,在这个对象析构的时候完成资源的释放。于是我们可以将上例改成如下形式:

#include <iostream>
#include <memory>

template<typename T>
class auto_release_ptr
{
public:
	auto_release_ptr(T *t) :_t(t){};
	~auto_release_ptr()
	{
		delete _t;
	};

	T * getPtr()
	{
		return _t;
	}

private:
	T *_t;
};

int main()
{
	for (int i = 1; i <= 10000000; i++)
	{
		auto arp = auto_release_ptr<int32_t>(new int32_t[3]);
		int32_t *ptr = arp.getPtr();
		ptr[0] = 1;
		ptr[1] = 2;
		ptr[2] = 3;
	}
	system("pause");
	return 0;
}

然后内存占用变成了这样:


太棒了,只用每次 new 的时候将其传给我们的模板类 auto_release_ptr 就可以防止内存泄露了!让我们来看看这是怎么实现的。

当我们使用 new 出一块内存的时候,我们将其传给了模板类 auto_release_ptr,再通过其实例的 getPtr() 方法得到了内存地址。auto_release_ptr 有一个数据成员在构造时完成了初始化并指向了 new 出来的空间,而在其析构函数中,我们使用 delete 来释放这块内存空间,于是我们 new 出来的资源便有了和 auto_release_ptr 对象一样的生命周期,并且会在其托管的 auto_release_ptr 对象生命周期结束时被释放。由于 ptr 与 auto_release_ptr  对象的定义是在一块的,所以它们的生命周期自然也是相同的,即便 ptr 被回收时我们也不用再担心其指向的内存空间没有被释放了。


当然,这里只是简单举个例子来说明RALL。RALL机制便是通过利用对象的自动销毁,使得资源也具有了生命周期,有了自动销毁(自动回收)的功能。

更多的如智能指针,lock_guard都利用了RALL机制来实现。



07-23
### C++ RAII 使用指南 RAII(Resource Acquisition Is Initialization)是一种 C++ 编程技术,其核心思想是将资源的获取和释放绑定到对象的生命周期上。通过构造函数获取资源,析构函数释放资源,确保资源在对象生命周期结束时自动释放,从而避免资源泄漏。 #### RAII 的基本原理 RAII 依赖于类的构造函数和析构函数来管理资源。构造函数负责获取资源,例如内存分配、文件打开或锁的获取;析构函数则负责在对象生命周期结束时释放资源。这种方式确保了即使在异常发生的情况下,资源也能正确释放,提高了程序的健壮性[^2]。 #### RAII 的典型应用场景 - **资源管理**:如文件句柄、网络连接、内存分配等。 - **锁管理**:在多线程编程中,使用 RAII 自动管理互斥锁的获取和释放,避免死锁问题。 - **状态管理**:在需要临时改变某些状态的场景中,确保状态在操作完成后恢复原状。 #### RAII 的实现方式 RAII 的实现通常涉及定义一个类,该类在构造函数中获取资源,在析构函数中释放资源。例如,管理一个文件句柄的 RAII 类可以如下实现: ```cpp #include <fstream> #include <iostream> class FileHandler { public: FileHandler(const std::string& filename) { file.open(filename); if (!file.is_open()) { throw std::runtime_error("Failed to open file"); } } ~FileHandler() { if (file.is_open()) { file.close(); } } std::ifstream& get() { return file; } private: std::ifstream file; }; ``` 在使用时,只需创建 `FileHandler` 的实例,文件会在对象生命周期结束时自动关闭: ```cpp try { FileHandler handler("example.txt"); // 使用文件 } catch (const std::exception& e) { std::cerr << "Exception: " << e.what() << std::endl; } ``` #### RAII 与标准库工具 C++ 标准库提供了多个 RAII 的实现,如 `std::unique_ptr` 和 `std::shared_ptr` 用于管理动态分配的内存,`std::lock_guard` 和 `std::unique_lock` 用于管理互斥锁。这些工具能够覆盖 80% 的常见场景,是优先选择的方案[^1]。 #### RAII 与现代 C++ 特性 现代 C++(如 C++11 及其后续版本)引入了许多特性,可以与 RAII 结合使用以提高代码的安全性和可读性。例如,智能指针和 lambda 表达式可以简化资源管理和异步操作的代码: ```cpp #include <memory> #include <vector> #include <algorithm> int main() { // 使用智能指针管理资源 std::unique_ptr<int> ptr(new int(10)); // 使用lambda表达式排序 std::vector<int> vec = {5, 3, 8, 1}; std::sort(vec.begin(), vec.end(), [](int a, int b) { return a < b; }); // 范围for循环遍历容器 for (auto& item : vec) { // 处理item } return 0; } ``` #### RAII 的最佳实践 - **避免手动管理资源**:尽量使用标准库提供的 RAII 工具,而不是手动管理资源的获取和释放。 - **设计通用 RAII 类**:在需要自定义 RAII 类时,确保类的设计通用且易于复用。 - **利用模板和继承**:在框架或库开发中,考虑使用 CRTP(Curiously Recurring Template Pattern)模式实现扩展性。 - **遵循团队共识**:对于临时需求,使用宏定义快速实现时,需确保团队共识,避免代码维护问题。 #### RAII 的优势 - **资源安全性**:确保资源在异常发生时也能正确释放。 - **代码简洁性**:减少手动资源管理的代码量,提高代码的可读性和可维护性。 - **异常安全性**:RAII 提供了异常安全的资源管理机制,避免资源泄漏。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值