RAII 标准
什么是RAII?
资源获取即初始化(资源析构即开释)
- RAII(Resoure Acquisition is Initialization)范式(即:目标结构的时分所需的资源应该在结构函数中初始化,而目标析构的时分应该开释资源)。RAII 告诉咱们应该应用类来封装和办理资源。
RAII 示例
- 哪些资源需求运用 RAII 机制维护?
分类 | 示例 | 适用规模 |
---|---|---|
STL | ||
智能指针 | ||
锁 |
不同场景引荐 RAII 方案
内存办理
// 手动办理
void func1(int len) {
int* buf = new int[len];
input_buffer(buf);
if (...) {
run(buf);
}
print_buffer(buf);
delete buf;
}
// vector 办理
void func2(int len) {
std::vector<int> buf;
buf.resize(len);
input_buffer(buf.data());
if (...) {
run(buf.data());
}
print_buffer(buf.data());
}
// unique_ptr 办理
void func3(int len) {
std::unique_ptr<int[]> buf(new int[len]);
input_buffer(buf.get());
if (...) {
run(buf.get());
}
print_buffer(buf.get());
}
- 引荐:
操作办理
RXScopeGuard
- 智能指针是对目标的内存进行办理,但关于操作层面并不便利处理(如文件封闭)。
class RXScopeGuard {
public:
explicit RXScopeGuard(std::function<void()> on_exit_scope)
: on_exit_scope_(on_exit_scope) {}
~RXScopeGuard() {
on_exit_scope_();
}
private:
std::function<void()> on_exit_scope_;
};
// 避免变量命名问题,经过 function & line 自动命名变量名
#define _RX_SCOPEGUARD_NAME_(name, line) name##line
#define RX_SCOPEGUARD_NAME_(line) _RX_SCOPEGUARD_NAME_(_on_exit_, line)
#define RX_ON_SCOPE_EXIT(callback) \
RXScopeGuard RX_SCOPEGUARD_NAME_(__LINE__)(callback)
- 示例
void func() {
std::fstream fs("filename", ios::binary);
RX_ON_SCOPE_EXIT( [&] { fs.close(); } );
// fs.read( ... );
int* buf = new int[__LINE__];
RX_ON_SCOPE_EXIT( [&] { delete[] buf; } );
// scanf(buf);
std::FILE* ff = std::fopen("filename2", "r");
RX_ON_SCOPE_EXIT( [&] { std::fclose(ff); } );
// std::fread(ff, ... );
}
-
长处:
-
缺陷
RXScopeGuard1
- 改善方案:运用函数指针
template <typename T,
typename Ref = typename std::add_lvalue_reference<T>::type,
typename U = typename std::conditional<std::is_pointer<T>::value, T, Ref>::type>
class RXScopeGuard1 {
public:
using OnExitScopeFunc = void (*)(U);
explicit RXScopeGuard1(OnExitScopeFunc on_exit_scope, U arg)
: on_exit_scope_(on_exit_scope), arg_(arg) {}
~RXScopeGuard1() {
on_exit_scope_(arg_);
}
private:
OnExitScopeFunc on_exit_scope_;
U arg_;
};
// 避免变量命名问题,经过 function & line 自动命名变量名
#define RX_SCOPEGUARD_NAME_(line) _on_exit_##line
#define RX_ON_SCOPE_EXIT_1(callback, obj) \
RXScopeGuard1<decltype(obj)> RX_SCOPEGUARD_NAME_(__LINE__)(callback, obj)
void rxOnExitScope_DeleteArray(char* buf) {
RX_LOG(kRXLSError) << "~ delete[] buf";
delete[] buf;
}
void rxOnExitScope_StdClose(std::FILE* file) {
RX_LOG(kRXLSError) << "~ std::fclose(file)";
std::fclose(file);
}
void rxOnExitScope_StdIfstreamClose(std::ifstream& ifs) {
RX_LOG(kRXLSError) << "~ ifs.close()";
ifs.close();
}
void func() {
char* buf = new char[128];
// RX_ON_SCOPE_EXIT_1(rxOnExitScope_DeleteArray, buf);
RX_ON_SCOPE_EXIT_1(
[](char* buf) {
RX_LOG(kRXLSError) << "~ delete[] buf";
delete[] buf;
},
buf);
{
std::ifstream ifs("./a.txt", std::ios::binary);
// RX_ON_SCOPE_EXIT_1(rxOnExitScope_StdIfstreamClose, ifs);
RX_ON_SCOPE_EXIT_1(
[](std::ifstream& ifs) {
RX_LOG(kRXLSError) << "~ ifs.close()";
ifs.close();
},
ifs);
// ifs.read( ... );
std::FILE* ff = std::fopen("./a.txt", "r");
// RX_ON_SCOPE_EXIT_1(rxOnExitScope_StdClose, ff);
RX_ON_SCOPE_EXIT_1(
[](std::FILE* file) {
RX_LOG(kRXLSError) << "~ std::fclose(file)";
std::fclose(file);
},
ff);
// std::fread( ... );
}
}
- 运转结果
~ ifs.close()
~ std::fclose(file)
~ delete[] buf
-
长处:
-
缺陷
RXScopeGuardList
- 运用 void* 数组保存输入参数的指针。
template<uint8_t kSize>
class RXScopeGuardList {
public:
using OnExitScopeFunc = void (*)(void**);
OnExitScopeFunc on_exit_scope_;
void* args_[kSize];
~RXScopeGuardList() {
on_exit_scope_(args_);
}
};
#define RX_ARG_T(t) t
#define RX_ARG_N(a1, a2, a3, a4, a5, N, ...) N
#define RX_ARG_N_HELPER(...) RX_ARG_T(RX_ARG_N(__VA_ARGS__))
// 计算可变参数的数量(必须在 [0, 5] 规模内)
#define RX_COUNT_ARG(...) RX_ARG_N_HELPER(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define _RX_SCOPEGUARD_NAME_(name, line) name##line
#define RX_SCOPEGUARD_NAME_(line) _RX_SCOPEGUARD_NAME_(_on_exit_, line)
#define RX_ON_SCOPE_EXIT_LIST(callback, arg, ...) \
RXScopeGuardList<RX_COUNT_ARG(arg, ##__VA_ARGS__)> RX_SCOPEGUARD_NAME_(__LINE__) { \
callback, arg, ##__VA_ARGS__ \
}
void func() {
char* buf = new char[128];
std::ifstream ifs("./a.txt", std::ios::binary);
std::FILE* ff = std::fopen("./a.txt", "r");
// args[] 的参数顺序等于输入参数的顺序
RX_ON_SCOPE_EXIT_LIST([](void** args){
// 1. ifs
RX_LOG(kRXLSError) << "~ ifs.close()";
static_cast<std::ifstream*>(args[0])->close();
// 2. ff
RX_LOG(kRXLSError) << "~ std::fclose(file)";
std::fclose(static_cast<std::FILE*>(args[1]));
// 3. buf
RX_LOG(kRXLSError) << "~ delete[] buf";
delete[] static_cast<char*>(args[2]);
}, &ifs, ff, buf);
// fs.read( ... );
// std::fread( ... );
}
-
长处:
-
缺陷:
- 正确的示例
void func() {
char* buf = new char[128];
RX_ON_SCOPE_EXIT_LIST([](void** args){
RX_LOG(kRXLSError) << "~ delete[] buf";
delete[] static_cast<char*>(args[0]);
};
std::ifstream ifs("./a.txt", std::ios::binary);
RX_ON_SCOPE_EXIT_LIST([](void** args){
RX_LOG(kRXLSError) << "~ ifs.close()";
static_cast<std::ifstream*>(args[0])->close();
};
std::FILE* ff = std::fopen("./a.txt", "r");
RX_ON_SCOPE_EXIT_LIST([](void** args){
RX_LOG(kRXLSError) << "~ std::fclose(file)";
std::fclose(static_cast<std::FILE*>(args[0]));
};
// fs.read( ... );
// std::fread( ... );
}
封装成 class
-
RXScopeGuard / RXScopeGuardList 作用域规模比较小,一般用于函数内部。
-
示例:音讯订阅和撤销订阅
class RXTest {
public:
void init() {
message_center = RXGlobalContext->message_center;
// 1. 订阅音讯
message_center->subscribe_message(handler1, RXMessageFilter{...});
message_center->subscribe_message(handler2, RXMessageFilter{...});
message_center->subscribe_message(handler3, RXMessageFilter{...});
}
RXTest() {
init();
}
~RXTest() {
// 2. 撤销订阅音讯
message_center->unsubscribe_all_messages(handler1);
message_center->unsubscribe_all_messages(handler2);
message_center->unsubscribe_all_messages(handler3);
}
private:
RXMessageCenter* message_center;
RXMessageHandler* handler1;
RXMessageHandler* handler2;
RXMessageHandler* handler3;
};
- RXMessageSubscriber
class RXMessageSubscriber {
public:
RXMessageSubscriber(RXMessageCenter* center, RXMessageHandler* handler)
: center_(center), handler_(handler) {}
~RXMessageSubscriber() {
// 封装了 撤销所有音讯订阅 的操作
center_->unsubscribe_all_messages(handler_);
}
void subscribe_message(RXMessageFilter filter,
RXMessageQueueInterface* queue = nullptr) {
center_->subscribe_message(handler_, filter, queue);
}
private:
RXMessageCenter* center_;
RXMessageHandler* handler_;
};
class RXTest {
public:
void init() {
message_center = RXGlobalContext->message_center;
// 1. 订阅音讯: 相当于封装了一个 unsubscribe_all_messages 操作
subscribers.emplace_back(message_center, handler1);
subscribers.back()->subscribe_message(RXMessageFilter{...});
subscribers.emplace_back(message_center, handler2);
subscribers.back()->subscribe_message(RXMessageFilter{...});
subscribers.emplace_back(message_center, handler3);
subscribers.back()->subscribe_message(RXMessageFilter{...});
}
RXTest() {
init();
}
private:
RXMessageCenter* message_center;
std::vector<RXMessageSubscriber> subscribers;
};
-
长处:
-
缺陷:
-
对资源收回有顺序要求的示例
class RXEngine {
public:
void init() {
message_center = new RXMessageCenter{};
message_queue = new RXMessageQueue{};
// 1. 订阅音讯
message_center->subscribe_message(handler1, RXMessageFilter{...});
message_center->subscribe_message(handler2, RXMessageFilter{...});
message_center->subscribe_message(handler3, RXMessageFilter{...},
message_queue);
}
void destory() {
// 2.1 撤销订阅音讯
message_center->unsubscribe_all_messages(handler1);
message_center->unsubscribe_all_messages(handler2);
message_center->unsubscribe_all_messages(handler3);
// 2.2 清空使命
delete message_queue;
// 2.3 移除音讯中心
delete message_center;
}
private:
RXMessageCenter* message_center;
RXMessageHandler* handler1;
RXMessageHandler* handler2;
RXMessageHandler* handler3;
RXMessageQueueInterface* message_queue;
std::unique_ptr<RXScopeGuardList<5>> scope_gurad_list_;
};
- 资源收回有顺序依靠
class RXEngine {
public:
void init() {
message_center = new RXMessageCenter{};
message_queue = new RXMessageQueue{};
// 1. 订阅音讯
message_center->subscribe_message(handler1, RXMessageFilter{...});
message_center->subscribe_message(handler2, RXMessageFilter{...});
message_center->subscribe_message(handler3, RXMessageFilter{...},
message_queue);
scope_gurad_list_ = std::make_unique<RXScopeGuardList<5>>(
[](void** args) {
RXMessageCenter* center = static_cast<RXMessageCenter*>(args[4]);
// 2.1 撤销订阅音讯
center->unsubscribe_all_messages(static_cast<RXMessageHandler*>(args[0]));
center->unsubscribe_all_messages(static_cast<RXMessageHandler*>(args[1]));
center->unsubscribe_all_messages(static_cast<RXMessageHandler*>(args[2]));
// 2.2 清空使命
delete static_cast<RXMessageQueueInterface*>(args[3]);
// 2.3 移除音讯中心
delete center;
}, handler1, handler2, handler3, message_queue, message_center);
}
void destory() {
scope_gurad_list_.reset();
}
private:
RXMessageCenter* message_center;
RXMessageHandler* handler1;
RXMessageHandler* handler2;
RXMessageHandler* handler3;
RXMessageQueueInterface* message_queue;
std::unique_ptr<RXScopeGuardList<5>> scope_gurad_list_;
};
总结
-
主张把资源的申请和开释放在一起实现。避免遗失或逻辑不一致导致资源走漏。
-
关于资源开释顺序有依靠的,需求留意收回方式(集中收回或许导致部分资源申请出错时,前面未维护的资源走漏)
-
引荐运用 RXScopeGuardList / 自定义的class 封装资源的收回操作(留意ut测试是否有走漏)。
-
引荐运用 std::unique_ptr > STL / RXScopeGuardList 进行内存办理