先看一个例子
#include
int main() {
// 申请内存
char *p = new char[4096];
// 。。。。。。。。。
// 这里省略了N行业务代码
// 。。。。。。。。。
// 释放内存
delete[] p;
return 0;
}
以上操作方式存在的隐患:
- 1. 可能由于中间业务代码太长,后面的delete[] p;会忘记;
- 2. 即使有后面的delete[] p;但是中间业务代码如果出现异常,比如数组访问越界等,那么将会导致最后的delete无法运行,内存得不到释放,程序运行久了之后将会内存溢出(服务器内存被撑爆或达到程序最大可使用的内存后被kill掉)。
怎么避免?RAII,它就是用来解决上述问题的。
我个人认为,随着RAII的提出,现代C++(C11以及之后)库大量采用RAII方式进行编写,现在已经是使用C++进行程序编写的最佳时机,可以兼顾高性能和高安全性。
RAII的定义
来自百科的定义:
RAII,也称为“资源获取就是初始化”,是c++用来管理资源、避免内存泄露的方法。
简单粗暴理解为:
资源获取或内存分配在构造函数中进行,释放在析构函数中进行。这样就不存在忘记释放资源或者内存,即使是程序出现异常,也能够得到安全释放。高安全、高安全、高安全、高安全(重要事情说三遍)。
对上述代码基于RAII实现
class SafeString {
private:
char *sPtr = nullptr;
public:
SafeString() = delete;
SafeString(const SafeString &) = delete;
SafeString& operator=(const SafeString &) = delete;
SafeString(int size) {
// 在构造函数初始化这里进行内存的申请
sPtr = new char[size];
}
~SafeString() {
// 在析构函数这里进行释放
if (sPtr != nullptr) {
delete[] sPtr;
sPtr = nullptr;
}
}
// 这里获取到字符串操作的指针,方便外部
char* getStrPtr() {return sPtr;}
};
int main() {
// 进行访问控制
SafeString sPtr(4096);
// 。。。。。。
// 这里省略了N行业务代码
// 。。。。。。
return 0;
}
个人项目案例背景
并发访问控制:
注册手机号不能重复问题:由于用户账号表支持手机号、邮箱、微信、。。。。。等方式进行注册,所以数据表不能对手机号字段设定唯一索引,为了避免一个手机号注册存在多条记录,采用Redis的set nx技术进行控制。
示例代码如下
class UtilViewGuard {
private:
string mViewKey;
bool mCanView;
public:
UtilViewGuard() = delete;
// 这里的viewKey对应案例分析就是phone号码
// 因为还可以适用于其它场景,所以这里用viewKey
UtilViewGuard(std::string viewKey) {
mViewKey = viewKey;
mCanView = false;
string viewValue;
// 设定60秒也会自动过期,多重保障
int ex = 60;
redisCommand(
// 这里假设hiRedis已经被正确的初始化
// 关于hiRedis的使用,可以看我之前分享
hiRedis,
// 这里的核心是nx,也就是key不存在才会设定
// 这样同一个时间就只会有一个手机号码可以注册
"set %s %s ex %d nx",
mViewKey.c_str(), viewValue.c_str(), ex
);
// 这里根据操作结果是否成功对mCanView进行赋值
// 限于篇幅,这里省略
}
~UtilAccessGuard() {
// 在析构中进行删除,这样操作完成,用户有可以尝试注册了
// 当然这里尝试注册会失败,哈哈哈,主要是一个思想
redisCommand(hiRedis, "del %s", mViewKey.c_str());
}
// 是否可以访问
bool canView() {return mCanView;}
};
int main() {
// 。。。。。。
// 这里假设对用户请求做了初步检查
// 。。。。。。
// 进行访问控制
UtilViewGuard uvg;
if (!uvg.canView()) {
// 这里如果不能访问则直接返回
return -1;
}
// 。。。。。。
// 这里进行进行注册业务代码以及其它代码
// 。。。。。。
return 0;
}
交流:
大家实际项目中哪些场景下需要自己去基于C++的RAII技术实现呢?