是否有可能在纯C中实现RAII?
我认为不可能以任何理智的方式,但也许可能使用某种肮脏的技巧.重载标准free函数会想到或者可能覆盖堆栈上的返回地址,这样当函数返回时,它会调用一些其他函数以某种方式释放资源?或者也许有一些setjmp/longjmp技巧?
这纯粹是学术上的兴趣,我无意写出这种不可移植和疯狂的代码,但我想知道这是否可能.
我们在C++中使用RAII的次数越多,我们就越发现自己的析构函数会进行非平凡的释放.现在,解除分配(终结,但是你想要调用它)可能会失败,在这种情况下,异常实际上是让楼上的任何人知道我们的释放问题的唯一方法.但是再说一次,抛出析构函数是一个坏主意,因为在堆栈展开期间可能会抛出异常.std::uncaught_exception()让你知道什么时候发生,但不是更多,所以除了让你在终止之前记录一条消息之外你没有太多可以做的,除非你愿意让你的程序处于未定义的状态,其中一些东西被解除分配/最终化而一些不是.
一种方法是使用无抛出析构函数.但在许多情况下,这只是隐藏了一个真正的错误.例如,我们的析构函数可能会因为抛出某些异常而关闭一些RAII管理的数据库连接,并且这些数据库连接可能无法关闭.这并不一定意味着我们可以在此时终止程序.另一方面,记录和跟踪这些错误并不是每个案例的真正解决方案; 否则我们就不需要开始例外了.使用无抛出析构函数,我们还发现自己必须创建应该在销毁之前调用的"reset()"函数 - 但这只会破坏RAII的整个目的.
另一种方法是让程序终止,因为这是你可以做的最可预测的事情.
有些人建议链接异常,以便一次可以处理多个错误.但老实说,我从来没有真正看到用C++完成的工作,我也不知道如何实现这样的东西.
所以它是RAII或例外.不是吗?我倾向于无抛出的破坏者; 主要是因为它保持简单(r).但我真的希望有一个更好的解决方案,因为,正如我所说,我们使用RAII的次数越多,我们发现自己越多地使用执行非平凡事情的dtors.
附录
我正在添加链接到我发现的有趣的主题文章和讨论:
我运行了一个示例程序,确实调用了堆栈分配对象的析构函数,但这是否由标准保证?
我听说C++ 14在C++标准库中引入垃圾收集器.这个功能背后的理由是什么?这不是RAII存在于C++中的原因吗?
宏很好.模板很好.几乎无论它的工作原理都很好.
这个例子是OpenGL; 但该技术是C++特有的,并且不依赖于OpenGL的知识.
精确问题:
我想要一个表达式E; 我不需要指定唯一名称; 这样,在定义E的地方调用构造函数,并在块E的末尾调用析构函数.
例如,考虑:
class GlTranslate {
GLTranslate(float x, float y, float z); {
glPushMatrix();
glTranslatef(x, y, z);
}
~GlTranslate() { glPopMatrix(); }
};
Run Code Online (Sandbox Code Playgroud)
手动解决方案
{
GlTranslate foo(1.0, 0.0, 0.0); // I had to give it a name
.....
} // auto popmatrix
Run Code Online (Sandbox Code Playgroud)
现在,我不仅有glTranslate,还有很多其他的PushAttrib/PopAttrib调用.我宁愿不必为每个var提出一个唯一的名称.是否存在涉及宏模板的一些技巧......或者其他会自动创建变量的变量,在定义点调用构造函数; 和块结束时调用的析构函数?
谢谢!
5 规则指出,如果一个类有一个用户声明的析构函数、复制构造函数、复制赋值构造函数、移动构造函数或移动赋值构造函数,那么它必须有其他 4 个。
但今天我突然明白了:你什么时候需要用户定义的析构函数、复制构造函数、复制赋值构造函数、移动构造函数或移动赋值构造函数?
在我的理解中,隐式构造函数/析构函数适用于聚合数据结构。但是,管理资源的类需要用户定义的构造函数/析构函数。
但是,不能将所有资源管理类都使用智能指针转换为聚合数据结构吗?
例子:
// RAII Class which allocates memory on the heap.
class ResourceManager {
Resource* resource;
ResourceManager() {resource = new Resource;}
// In this class you need all the destructors/ copy ctor/ move ctor etc...
// I haven't written them as they are trivial to implement
};
Run Code Online (Sandbox Code Playgroud)
对比
class ResourceManager {
std::unique_ptr<Resource> resource;
};
Run Code Online (Sandbox Code Playgroud)
现在示例 2 的行为与示例 1 完全相同,但所有隐式构造函数都可以工作。
当然,你不能 copy ResourceManager,但如果你想要不同的行为,你可以使用不同的智能指针。
关键是当智能指针已经有那些隐式构造函数可以工作时,你不需要用户定义的构造函数。
我认为拥有用户定义的构造函数的唯一原因是:
你不能在一些低级代码中使用智能指针(我非常怀疑这种情况)。
您正在自己实现智能指针。
但是,在普通代码中,我看不出有任何理由使用用户定义的构造函数。
我在这里错过了什么吗?
自从我使用Java以来已经至少5年了,当时,只要你想分配一个需要清理的对象(例如套接字,数据库句柄),你就必须记得添加一个finally块并调用清理方法.那里.
相比之下,在C++(或其他对象生命周期是确定性的语言,例如Perl)中,类实现者将定义一个析构函数,只要该类的对象超出范围,该函数就会执行清理.这种方法的优点是对象的用户不会忘记清理它 - 即使抛出异常,也会自动调用析构函数.这种方法以RAII的相当可怕的名字命名 - "资源获取是初始化".
根据我的经验,做"RAII方式"的事情在不必担心是否以及何时发生资源解除分配方面为我节省了大量精神开销.我们正在考虑将Java用于一个中型项目,我想知道自从我上次查看它以来,语言中添加的许多新功能是否存在某种确定性破坏.(我希望因为我的抱怨"Java没有RAII" 在这个帖子上被斥责,但到目前为止我还没能通过谷歌搜索找到任何细节.)
所以,如果有人能够指出一些关于如何在Java中实现这一目标的介绍性材料,那就太棒了!
在下面的C++代码中,我保证在//更多代码执行后调用~obj()析构函数?或者如果编译器检测到它没有被使用,是否允许编译器更早地销毁它?
{
SomeObject obj;
... // More code
}
Run Code Online (Sandbox Code Playgroud)
我想使用这种技术来节省我必须记住在块的末尾重置一个标志,但我需要为整个块保持设置的标志.
我想学习如何在c ++中使用RAII.我想我知道它是什么,但不知道如何在我的程序中实现它.快速谷歌搜索没有显示任何好的教程.
有没有人有任何好的链接教我RAII?
我注意到RAII在Stackoverflow上得到了很多关注,但在我的圈子里(主要是C++),RAII非常明显,就像问什么是类或析构函数一样.
所以我真的很好奇,如果那是因为我每天都被硬核C++程序员所包围,并且RAII一般不是众所周知的(包括C++),或者如果Stackoverflow上的所有这些问题都归因于事实我现在正在与没有使用C++成长的程序员联系,而在其他语言中,人们只是不使用/了解RAII?