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 进行内存办理