问题是:您能否仅使用我在此问题底部提供的资源帮助我更好地理解C语言(而不是c ++)中的RAII宏?我试图在脑海中分析它,以便理解它所说的内容以及它是如何理解的(在我看来它没有意义)。语法很难。 问题的焦点是:我无法阅读和理解奇怪的语法及其在C语言中的实现。 例如,我可以轻松阅读,理解和分析(对我来说)以下交换宏:
#define myswap(type,A,B) {type _z; _z = (A); (A) = (B); (B) = _z;}
(以下段落取自本书:理解C指针)
在C语言中,GNU编译器提供非标准扩展 支持RAII。
GNU扩展使用名为RAII_VARIABLE的宏。它声明了一个 变量并与变量关联:
变量超出范围时执行的函数
宏如下所示:
#define RAII_VARIABLE(vartype,varname,initval,dtor) \ void _dtor_ ## varname (vartype * v) { dtor(*v); } \ vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval)
示例:
void raiiExample() { RAII_VARIABLE(char*, name, (char*)malloc(32), free); strcpy(name,"RAII Example"); printf("%s\n",name); } int main(void){ raiiExample(); }
执行此功能时,将显示字符串“RAII_Example”。在不使用GNU扩展的情况下可以实现类似的结果。
答案 0 :(得分:4)
好的,让我们逐行查看宏观部分
#define RAII_VARIABLE(vartype,varname,initval,dtor) \
第一行当然是宏名称加上其参数列表。这里没什么意外,我们似乎传递一个类型,一个令牌名称,一些表达式来初始化一个变量,以及一些希望最终被调用的析构函数。到目前为止,这很容易。
void _dtor_ ## varname (vartype * v) { dtor(*v); } \
第二行声明了一个函数。它采用提供的令牌varname
并在其前面添加前缀_dtor_
(##
运算符指示预处理器将两个令牌融合到一个令牌中)。此函数将指向vartype
的指针作为参数,并使用该参数调用提供的析构函数。
这种语法在这里可能会出乎意料(比如使用##
运算符,或者它依赖于声明嵌套函数的能力),但它还没有真正的魔力。魔术出现在第三行:
vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval)
这里声明了变量,没有__attribute__()
这看起来非常简单:vartype varname = (initvar)
。神奇的是__attribute__((cleanup(_dtor_ ## varname)))
指令。它指示编译器确保在变量超出范围时调用提供的函数。
__attribute__()
语法是编译器提供的语言扩展,因此您可以深入了解实现定义的行为。您不能依赖提供相同__attribute__((cleanup()))
的其他编译器。许多人可能会提供它,但没有必要。一些较旧的编译器甚至可能根本不知道__attribute__()
语法,在这种情况下,标准过程是#define __attribute__()
为空,从代码中剥离所有__attribute__()
声明。您不希望RAII变量发生这种情况。因此,如果您依赖__attribute__()
,请知道您已经失去了使用任何标准符合编译器进行编译的能力。
答案 1 :(得分:3)
当然,您可以在不使用RAII的情况下实现任何目标。 RAII用例它不必考虑明确释放资源。像:
这样的模式leader
需要你注意释放内存,如果没有,你会有内存泄漏。由于正确发布资源并不总是很容易,RAII为您提供了一种自由化的方式:
void f() {
char *v = malloc(...);
// use v
free v;
}
有趣的是,无论执行的路径是什么,都将发布ressource。因此,如果您的代码是一种意大利面条代码,充满了复杂的条件和测试等,RAII可以让您放心释放...
答案 2 :(得分:3)
语法有点棘手,因为__attribute__ ((cleanup))
期望传递一个将指针指向变量的函数。来自GCC documentation(强调我的):
该函数必须带一个参数,一个指向兼容类型的指针 使用变量。函数的返回值(如果有的话)是 忽略。
请考虑关注不正确的示例:
char *name __attribute__((cleanup(free))) = malloc(32);
像这样实现它会简单得多,但是在这种情况下free
函数隐含地将指针带到name
,其类型为char **
。你需要一些方法来强制传递正确的对象,这就是RAII_VARIABLE
类似函数的宏的想法。
RAII_VARIABLE
的简化和非通用化身将是定义函数,比如raii_free
:
#include <stdlib.h>
void raii_free(char **var) { free(*var); }
int main(void)
{
char *name __attribute__((cleanup(raii_free))) = malloc(32);
return 0;
}