|
我们将内存简单的分为栈内存和堆内存。
栈上的内存不需要考虑释放问题,而堆内存需要手动释放。一般来讲是以下的流程。
A* p = new A();
// do something
delete p;
通常我们会记得前两步,而忘记第三部,释放资源。
当然可以说,new 一定要和delete配对。
但是在某些情况下,我们会提前跳出,然后忘记匹配一个delete
void foo(int n) {
A* p = new A();
if (n > 1) {
/* ... */
return;
}
delete p;
}
当你处理错误,或者一些其它的事情,需要提前结束函数/作用域,这个时候,为了保证资源的释放,你需要每一个分支都加个delete 。
这样是比较麻烦的,所以这里引入RAII。
RAII,全称资源获取即初始化
RAII要求,资源的有效期与持有资源的对象的生命期严格绑定,即由对象的构造函数完成资源的分配(获取),同时由析构函数完成资源的释放。在这种要求下,只要对象能正确地析构,就不会出现资源泄漏问题。 emmmm,简单来说就是,无论怎么样,结束函数都会调用析构函数,所以我们在析构函数里面写delete。
也就是让编译器在结束函数的每一个分支上,加上析构函数,而我们在析构函数里面delete。
简单的写一下就是这个样子
struct Raii{
A* p;
Raii(A* _p) : p{ _p } {};
~Raii() {
delete p;
}
};
void foo() {
Raii ptr(new A());
}
这样析构函数会帮我们自动释放的。
ok,但是我们必然不会为了每一个类都去写一个RAII的管理类,所以用模板来生成吧。
template<typename T>
class RAII {
private:
T* data;
public:
RAII() : data(nullptr) {}
explicit RAII(T* rhs) : data{ rhs } {};
~RAII() {
delete data;
}
T* operator ->()const {
return data;
}
};
这里重载了 -> 来方便我们调用
struct A {
int x, y, z;
~A() {
cout << &#34;~A()&#34; << endl;
}
};
这样使用
RAII<A> ptr(new A{ 114,514,1919810 });
cout << ptr->x << endl;
<hr/>如果只是为了不要忘记释放资源,上面就够了,但是这里我们需要引入所有权的概念
如果你对所有权没有概念,推荐阅读 严格鸽:现代C++学习 —— 为什么需要std::move
其实下面的内容就和unique_ptr 一样了。 首先,我们这种写法,是独占所有权的。
RAII<A> p1(new A{ 114,514,1919810 });
RAII<A> p2 = p1;
上面这种情况,可以理解为,p1,p2同时有了 A 的所有权。

当我们析构函数释放的时候,显然这个内存,会被我们释放两次。
所以,我们需要用move来移交所有权。

在加上一些东西吧
template<typename T>
class RAII {
private:
T* data;
public:
RAII() : data(nullptr) {}
explicit RAII(T* rhs) : data{ rhs } {};
~RAII() {
if(data)delete data;
}
T* operator ->()const {
return data;
}
RAII(const RAII<T>&) = delete;
RAII(RAII<T>&& rhs) {
data = rhs.data;
rhs.data = nullptr;
}
RAII& operator = (const RAII&) = delete;
void operator = (RAII<T>&& rhs) {
data = rhs.data;
rhs.data = nullptr;
}
};
这里,我们把有关拷贝的函数都删除了。

但是我们可以通过move来移交所有权。

这样的代码
RAII<A> p1(new A{ 114,514,1919810 });
cout << &#34;here&#34; << endl;
输出
here
~A() 在析构函数在作用域的最后
而这样的代码
RAII<A> p1(new A{ 114,514,1919810 });
{
RAII<A>p3 = move(p1);
}
cout << &#34;here&#34; << endl;
输出
~A()
here 我们利用move,把所有权交给了{}里面的一个RAII类,缩窄了其生命周期。
好,那么问题又来了,如何把RAII类作为函数的参数?
这里有个回答就很好
https://www.zhihu.com/question/534389744/answer/2505999713

看情况使用第一条和第三条

 |
|