C++:实现RAII机制

发布时间 2023-04-03 12:44:15作者: karinto

RAII,也称资源获取即初始化,要求资源的有效期与持有资源的对象的生命期严格绑定,不会出现内存泄漏等问题。

我们尝试将指针封装到RAII类中,实现自动析构。

#include <iostream>

using namespace std;

template<typename T>
class RAII {
public:
	RAII() :data(nullptr) {}
	explicit RAII(T* rhs) :data(rhs) {}
	~RAII() {
		delete data;
	}
	T* operator ->() const {
		return data;
	}
private:
	T* data;
};

struct A {
	int x, y, z;
	~A() {
		cout << "~A();" << endl;
	}
};

int main() {
	RAII<A> ptr(new A{ 123, 456, 789 });
	cout << ptr->x << " " << ptr->y << " " << ptr->z << endl;

	return 0;
}

  

此时我们面临一个问题,此时调用以下代码:

RAII<A> ptr(new A{ 123, 456, 789 });
RAII<A> ptr2 = ptr;

ptr和ptr2中的data同时指向了同一个地址,换句话说,同时有了A的所有权。

程序退出时,析构函数会释放两次内存。

我们可以通过move移交所有权,确保只有一个指针指向A,减少进程退出时的内存释放次数。

以下代码禁用了拷贝构造函数:

template<typename T>
class RAII {
public:
	RAII(const RAII<T>&) = delete; //禁用拷贝构造
	RAII(RAII<T>&& rhs) { //万能引用, 支持move
		data = rhs.data;
		rhs.data = nullptr;
	}

	RAII& operator = (const RAII&) = delete;
	void operator = (RAII<T>&& rhs) {
		data = rhs.data;
		rhs.data = nullptr;
	}
};

int main(){
    	RAII<A> ptr(new A{ 123, 456, 789 });
	RAII<A> ptr2 = move(ptr);
}

  

源代码:

#include <iostream>

using namespace std;

template<typename T>
class RAII {
public:
	RAII() :data(nullptr) {}
	RAII(const RAII<T>&) = delete; //禁用拷贝构造
	RAII(RAII<T>&& rhs) { //万能引用, 支持move
		data = rhs.data;
		rhs.data = nullptr;
	}
	explicit RAII(T* rhs) :data(rhs) {}
	~RAII() {
		delete data;
	}

	RAII& operator = (const RAII&) = delete;
	void operator = (RAII<T>&& rhs) {
		data = rhs.data;
		rhs.data = nullptr;
	}

	T* operator ->() const {
		return data;
	}
private:
	T* data;
};

struct A {
	int x, y, z;
	~A() {
		cout << "~A();" << endl;
	}
};

int main() {
	RAII<A> ptr(new A{ 123, 456, 789 });
	cout << ptr->x << " " << ptr->y << " " << ptr->z << endl;

	RAII<A> ptr2 = move(ptr);

	return 0;
}

  执行结果:

123 456 789
~A();

我们看到虚构函数~A()只执行了一次。

关于生命周期相关问题本文章不做讨论。

 

原文:https://zhuanlan.zhihu.com/p/600337719

作者:严格鸽