「编程模型」C++ 封装资源

用户头像
顿晓
关注
发布于: 2020 年 06 月 11 日
「编程模型」C++封装资源

引子

最近 2 个月在做一个嵌入式 C++ 项目,Soc 提供的 API 都是 C 接口的,剩下的业务功能开发就全靠自己动手实现了。一般这类项目,有直接用 C 开发的,也有用 C++ 封装一下来降低复杂度。

场景

这类 C 接口中,有很大一部分是资源管理和操作的,这里的资源一般指使用前需要申请,使用后需要释放的某种有限的东西。操作系统中带有 Open、Close类似接口的都属这类,如文件、网络等,当然也有自己封装的资源,以及第三方库的。



使用资源,最麻烦的就是手动管理,项目复杂时,这里就会成为 Bug 之源。操作资源的基础版本是过程式的,全手动管理,也是一般 C 语言的惯用做法。



int workflow_a_b() {
int handle_a = -1;
int handle_b = -1;
int result = resource_a_open(&handle_a);
if (result < 0) {
return -1;
}
result = resource_a_op_1(handle_a);
if (result < 0) {
goto FAIL1;
}
result = resource_b_open(&handle_b);
if (result < 0) {
goto FAIL1;
}
result = resource_b_op_1(handle_b);
if (result < 0) {
goto FAIL2;
}
FAIL2:
resource_b_close(handle_b);
FAIL1:
resource_a_close(handle_a);
}



以上代码只是示意下使用场景,实际项目中的情况会比这个复杂几倍,一个子系统的出错处理轻松达 FAIL7。



类似这样的代码,最大的缺点就是没有可扩展性,只能为特定的场景量身定制;还有就是烧脑,需要很仔细的分析和检查;当然,如果不想烧脑,那就需要花费大量时间来调试,最终也能调出来。

解决方案

C++ 类的构造函数和析构函数,刚好能对应资源的申请和释放。这样,可以通过控制类实例的作用域来让资源的申请和释放自动化,隐藏在资源类的内部,对于资源操作的逻辑不可见。最终,达到逻辑表达简单直接。



下面是一个组合 2 个资源类的例子,代码可以粘贴到https://wandbox.org 在线运行。



#include <iostream>
using namespace std;
class Resource {
public:
Resource(size_t size) : size(size), buffer(NULL) {
init(size);
}
~Resource() {
release();
}
void init(size_t size) {
if (size > 250) {
throw "too large!";
}
this->size = size;
this->buffer = new int[size];
cout << "init size " << this->size << endl;
}
void release() {
cout << "release size " << this->size << endl;
if (buffer != NULL) {
delete[] buffer;
buffer = NULL;
}
this->size = 0;
}
void DoubleSize() {
size_t size = this->size;
release();
init(size * 2);
}
size_t Size() {
return size;
}
private:
size_t size;
int *buffer;
};
class ResourceA: public Resource {
public:
ResourceA(size_t size) : Resource(size) {
cout << "ResourceA ctor\n";
}
~ResourceA() {
cout << "ResourceA dtor\n";
}
};
class ResourceB: public Resource {
public:
ResourceB(size_t size) : Resource(size) {
cout << "ResourceB ctor\n";
}
~ResourceB() {
cout << "ResourceB dtor\n";
}
};
class ResourceAB {
public:
ResourceAB(): a(10), b(100) {
cout << "ResourceAB ctor\n";
}
~ResourceAB() {
cout << "ResourceAB dtor\n";
}
void DoubleSize() {
a.DoubleSize();
b.DoubleSize();
}
void Size() {
std::cout << "a " << a.Size() << "; b " << b.Size() << std::endl;
}
private:
ResourceA a;
ResourceB b;
};
int main()
{
ResourceAB ab;
try {
ab.Size();
ab.DoubleSize();
ab.Size();
ab.DoubleSize(); // 这里会触发异常条件
ab.Size();
}catch(const char* msg) {
cerr << msg << endl;
ab.Size();
}
}
init size 10
ResourceA ctor
init size 100
ResourceB ctor
ResourceAB ctor
a 10; b 100
release size 10
init size 20
release size 100
init size 200
a 20; b 200
release size 20
init size 40
release size 200
too large!
a 40; b 0
ResourceAB dtor
ResourceB dtor
release size 0
ResourceA dtor
release size 40



发布于: 2020 年 06 月 11 日 阅读数: 74
用户头像

顿晓

关注

因观黑白愕然悟,顿晓三百六十路。 2017.10.17 加入

视频号「编程日课」 一个不爱编程的程序员, 一个用软件来解决问题的工程师, 一个有匠心的手艺人。

评论

发布
暂无评论
「编程模型」C++封装资源