一、RALL是什么?
RALL是一种编程思想,对于一些重要资源(申请的空间,网络连接,互斥量等)我们需要在申请使用后将其释放掉,不然会造成资源的浪费。而RALL就是释放资源的一种方式,利用c++的特性,在构造函数中获取资源,析构函数中释放资源,利用对象控制资源的生存。
例子:
class sem
{
public:
sem()
{
if (sem_init(&m_sem, 0, 0) != 0)
{
throw std::exception();
}
}
sem(int num)
{
if (sem_init(&m_sem, 0, num) != 0)
{
throw std::exception();
}
}
~sem()
{
sem_destroy(&m_sem);
}
bool wait()
{
return sem_wait(&m_sem) == 0;
}
bool post()
{
return sem_post(&m_sem) == 0;
}
private:
sem_t m_sem;
};
class locker
{
public:
locker()
{
if (pthread_mutex_init(&m_mutex, NULL) != 0)
{
throw std::exception();
}
}
~locker()
{
pthread_mutex_destroy(&m_mutex);
}
bool lock()
{
return pthread_mutex_lock(&m_mutex) == 0;
}
bool unlock()
{
return pthread_mutex_unlock(&m_mutex) == 0;
}
pthread_mutex_t *get()
{
return &m_mutex;
}
private:
pthread_mutex_t m_mutex;
};
二、智能指针
什么是智能指针?
利用RALL思想实现对申请空间的自动销毁,通过几个模板函数:unique_ptr,shared_ptr,auto_ptr,weak_ptr来实现。
(1)shared_ptr:
共享的智能指针,允许多个智能指针可以指向同一块资源,并且保证共享的资源只会被释放一次。
原理:内部维护了一个引用计数,当有指针引用空间时计数+1,析构时计数-1,当计数为0则释放资源。
实现:
这里将资源的管理和构造方法分开实现,实现智能指针需要,无参构造,构造函数,拷贝构造,移动构造,拷贝赋值,移动赋值,重构*和=。
代码如下(示例):
#pragma once
#include<iostream>
using namespace std;
template<class T>
class Ref
{
int r_count = 0;//当前资源计数
T* object = nullptr; //被管理的堆区内存
public:
Ref(T *target) :object(target)
{
r_count++;
}
//引用计数加1
inline void increase()
{
r_count++;
}
//引用计数减一并且判断是否要释放掉原始堆区内存
inline void reduce()
{
r_count--; //引用计数减一
if (r_count == 0) //如果引用计数减为0释放被管理的堆区内存和自己
{
delete object;
delete this;
}
}
T *get()
{
return object;
}
int getCount()
{
return r_count;
}
};
//共享智能指针需要的方法:
/*
无参构造,传递指针构造,拷贝构造,移动构造,拷贝赋值,移动赋值
reset()替换对象 reset()销毁对象
operator*() operator->()
get()获取原始指针
use_count 获得引用计数
*/
template<class T>
class Share_ptr
{
Ref<T> * ref = nullptr; //Ref使用模板,定义其指针时需加上模板参数<T>
public:
Share_ptr() = default;
~Share_ptr()
{
if (ref) ref->reduce(); //引用计数减一
}
Share_ptr(T *newP)
{
cout << "---------------------调用构造函数-----------------" << endl;
ref = new Ref<T>(newP); //new申请空间同时调用构造函数,new申请无需指定空间大小()中的要走 前面类型的构造
}
Share_ptr(const Share_ptr &other)
{
cout << "------------------调用拷贝构造-----------------" << endl;
this->ref = other.ref;
if(ref) ref->increase(); //引用计数加1
}
Share_ptr( Share_ptr &&other)
{
cout << "------------------调用移动构造-----------------"<<endl;
ref = other.ref;
other.ref = nullptr;
}
Share_ptr& operator=(const Share_ptr &other)
{
cout << "-------------------------调用赋值函数-----------------------"<<endl;
if (ref)
{
ref->reduce();
}
ref = other.ref;
if (ref)
{
ref->increase();
}
return *this;
}
Share_ptr& operator =(Share_ptr &&other)
{
cout << "--------------------------调用移动赋值------------------------"<<endl;
if (ref)
{
ref->reduce();
}
ref = other.ref;
other.ref = nullptr;
return *this;
}
T& operator*()
{
return *ref->get();
}
T* operator->()
{
if (ref)
{
return ref->get();
}
}
int use_count()
{
if (ref) return ref->getCount();
else return 0;
}
};
这里并没有考虑多线程问题,多线程中对引用计数操作需要加锁。
注意:使用拷贝构造实则时复制,引用计数会增加,而移动构造不会,他是转让权限。
share_ptr初始化的方法:
1.构造函数初始化:
2.make_shared初始化:
(2)unique_ptr
该指针直接禁用了拷贝,赋值以及移动构造,是一个独占性指针。
总结
RALL是重要的编程思想,利用c++的特性实现自动管理资源,重要应用为智能指针,share_ptr等。