一、RALL简介
RALL是resource acquisition is initialization的缩写,意思是”资源获取即初始化”。它是C++之父Bjarne Stroustrup提出的设计理念,其核心是把资源和对象的声明周期绑定,对象创建获取资源,对象销毁释放资源。在RALL的指导下,C++把底层的资源管理问题提升到了对象声明周期管理的更高层次。
二、RALL机制
- 使用C++时,最让人头疼的便是内存管理,但却又正是对内存高度可操作性给了C++程序员极大自由和装逼资本。
- 当我们在堆区new出一块内存空间,在使用完之后,如果不使用delete来释放这块资源则将导致内存泄漏,这在中大型项目中极具有破坏性。但人无完人,我们并不能保证每次都记得释放无法再次获取到且不再使用的内存,下面给出一个例子,让大家看看忘记释放资源造成内存泄漏多么恐怖!!
#include<iostream>
#include<memory>
int main()
{
for (int i = 0; i <= 10000000; i++)
{
int* ptr = new int(3);
ptr[0] = 1;
ptr[1] = 2;
ptr[2] = 3;
//delete ptr //忘记释放内存
}
return 0;
}
测试结果(未释放内存):
运行程序并打开资源管理器,可以发现这么简单的程序竟然占用了11点几的MB内存,所以大家千万要记得释放内存。
三、智能指针的介绍
在 C++ 中没有垃圾回收机制,必须自己释放分配的内存,否则就会造成内存泄露。解决这个问题最有效的方法是使用智能指针(smart pointer)。智能指针是存储指向动态分配(堆)对象指针的类,用于生存期的控制,能够确保在离开指针所在作用域时,自动地销毁动态分配的对象,防止内存泄露。智能指针的核心实现技术是引用计数,每使用它一次,内部引用计数加1,每析构一次内部的引用计数减1,减为0时,删除所指向的堆内存。
C++11提供了三种智能指针,使用这三种智能指针需导入memory的头文件。
- std::shared_ptr:共享智能指针。
- std::unique_ptr:独占智能指针。
- std::weak_ptr:弱引用智能指针,它不共享指针,不能操作资源,是用来监视std::shared_ptr的。
四、std::unique_ptr的使用
std::unique_ptr 是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,可以通过它的构造函数初始化一个独占智能指针对象,但是不允许通过赋值将一个 unique_ptr 赋值给另一个 unique_ptr。
// 通过构造函数初始化对象
unique_ptr<int> ptr1(new int(10));
// error, 不允许将一个unique_ptr赋值给另一个unique_ptr
unique_ptr<int> ptr2 = ptr1;
std::unique_ptr不允许复制,但是可以通过函数返回给其他std::unique_ptr,还可以通过std::move来转移给其他的std::unique_ptr,这样原始指针的所有权就被转移了,这个原始指针还是被独占的。
#include <iostream>
#include <memory>
using namespace std;
unique_ptr<int> func()
{
return unique_ptr<int>(new int(520));
}
int main()
{
// 通过构造函数初始化
unique_ptr<int> ptr1(new int(10));
// 通过转移所有权的方式初始化
unique_ptr<int> ptr2 = move(ptr1);
unique_ptr<int> ptr3 = func();
return 0;
}
unique_ptr的指针类有一个reset方法,函数原型如下:
void reset( pointer ptr = pointer() ) noexcept;
使用reset方法可以让unique_ptr解除对原始内存管理,也可以用来初始化一个独占智能指针。
int main()
{
unique_ptr<int> ptr1(new int(10));
unique_ptr<int> ptr2 = move(ptr1);
ptr1.reset();
ptr2.reset(new int(250));
return 0;
}
- ptr1.reset(): 解除对原始内存的管理。
- ptr2.reset(new int(250)): 重新指定智能指针管理的原始内存。
如果想要获取独占智能指针管理原始地址,可以调用get()方法,函数原型如下:
pointer get() const noexcept;
获取原始地址的测试代码:
#include <iostream>
#include <memory>
using namespace std;
int main()
{
unique_ptr<int> ptr1(new int(10));
unique_ptr<int> ptr2 = move(ptr1);
ptr2.reset(new int(250));
cout << *ptr2.get() << endl; // 得到内存地址中存储的实际数值 250
return 0;
}
测试代码的结果: