当前位置: 首页 > 知识库问答 >
问题:

C++RAII与延迟?

施恩
2023-03-14

我最近开始学习C++,以前我是用围棋编程的。

我最近被告知不应该使用new,因为抛出的异常可能会导致分配的内存不是freeD并导致内存泄漏。一个流行的解决方案是RAII,我找到了一个很好的解释为什么要使用RAII以及它在这里是什么。

然而,从Go开始,整个RAII的事情似乎是不必要的复杂。Go有一个叫做defer的东西,它以一种非常直观的方式解决了这个问题。您只需将作用域结束时要html" target="_blank">执行的操作包装在defer()中,例如defer(free(ptr))defer(close_file(f)),它将自动发生在作用域结束时。

我做了一个搜索,找到了两个源代码,它们试图在这里和这里实现C++中的延迟功能。两个结果都是几乎完全相同的代码,也许其中一个复制了另一个。它们在这里:

推迟实施1:

template <typename F>
struct privDefer {
    F f;
    privDefer(F f) : f(f) {}
    ~privDefer() { f(); }
};

template <typename F>
privDefer<F> defer_func(F f) {
    return privDefer<F>(f);
}

#define DEFER_1(x, y) x##y
#define DEFER_2(x, y) DEFER_1(x, y)
#define DEFER_3(x)    DEFER_2(x, __COUNTER__)
#define defer(code)   auto DEFER_3(_defer_) = defer_func([&](){code;})

推迟实施2:

template <typename F>
struct ScopeExit {
    ScopeExit(F f) : f(f) {}
    ~ScopeExit() { f(); }
    F f;
};

template <typename F>
ScopeExit<F> MakeScopeExit(F f) {
    return ScopeExit<F>(f);
};

#define SCOPE_EXIT(code) \
    auto STRING_JOIN2(scope_exit_, __LINE__) = MakeScopeExit([=](){code;})

我有两个问题:

>

  • 在我看来,这个defer本质上做的是与RAII相同的事情,但更整洁、更直观。有什么区别,您看到使用这些defer实现有什么问题吗?

    我真的不明白#define部分对上面的这些实现做了什么。两者有什么区别,是不是其中一个更可取?

  • 共有1个答案

    赵昊阳
    2023-03-14

    你说的很多都是基于观点的,所以我要从我自己的观点开始。

    在C++世界中,我们期望RAII。如果你想和其他开发人员相处得很好,你俩都会遇到这种情况,如果你决定用一种不同的方式来做某事,仅仅因为它是你习惯的,你就会违背标准。

    此外,C++开发人员不使用FOPEN:-)。C++标准库包含了非常好的支持RAII的类,我们使用它们。因此,必须实现RAII实际上意味着在可能的情况下正确选择现有的标准类,或者确保您的对象与RAII兼容。

    我几乎不用重新设计代码来实现RAII。我选择的类会自动处理。

    因此,尽管您所展示的代码很有趣,但它实际上比RAII更复杂。每次你使用FOPEN的时候,你也要记得做你的延迟的事情。使用std::ifstream或std::ofstream是不是更容易?那就已经为你处理好了。(这也适用于您的代码必须在现场实现RAII的其他时候。这已经通过选择正确的类来实现了。)

    所以,不,不是更整洁更直观,因为你要记得去做。选对类,就不用记了。

    至于#definition--它们只是用来确保变量具有唯一的名称,并快捷化defer类的构造函数。

     类似资料:
    • 我在C类中有一个OpenGL对象。因为我正在使用RAII,所以我想让析构函数删除它。所以我的课看起来像: 这似乎有效。但每当我执行以下操作时,我在使用OpenGL时就会出现各种错误: 发生什么事? 注意:这是试图建立一个规范的答案,这些问题。

    • Rust 的变量不只是在栈中保存数据:它们也占有资源,比如 Box<T> 占有堆中的内存。Rust 强制实行 RAII(Resource Acquisition Is Initiallization,资源获取即初始化),所以任何一个对象在离开作用域时,它的析构器(destructor)都被调用以及它的资源都被释放。 这种行为避免了资源泄露(resource leak)的错误,所以你再也不用手动释放

    • 在C#中,我有以下两个简单的例子: 第一个示例创建一个打印“开始”的任务,等待5秒钟打印“完成”,然后结束任务。我等待任务完成,然后打印“全部完成”。当我运行测试时,它会按预期运行。 第二个测试应该具有相同的行为,只是由于使用了async和Wait,任务内部的等待应该是非阻塞的。但是这个测试只打印“开始”,然后立即打印“全部完成”和“完成”,永远不会打印。 我不知道我为什么会有这样的行为:S非常感

    • 我正在使用Cassandra 2.2并使用可插拔指标将Cassandra指标发送到Graphite, > 我在组织.apache.cassandra.metrics.Column家庭中搜索过,看到在“排量”和“读数总计”中都有一个属性“计数”,这两个计数属性之间有什么区别? 我的主要目标是获得每次读/写的延迟,您如何建议我获得它? 谢谢!

    • 问题内容: 在以下情况下,实现C ++ / Java IPC的最佳方法是什么? 我有两个程序需要相互通信,一个是用C ++编写的,另一个是用Java编写的。两者都在同一台计算机上运行。 程序相互发送消息。消息通常很短(少于几百个字节),但是大小可能为100KB或更大。 不需要确认消息(即不需要像HTTP这样的请求/响应模型)。例如,C 程序向Java程序发送一条消息,而Java程序可以在以后的某个

    • 本文向大家介绍C#中Linq延迟查询的例子,包括了C#中Linq延迟查询的例子的使用技巧和注意事项,需要的朋友参考一下 提出问题 下面所给代码编译时正常,但是执行时会出错,请指出程序在执行时能够执行到编号为(1)(2)(3)的代码行中的哪一行。 问题分析 其实,发现问题并不难,很显然在执行代码“it => it.Name.Trim()”时会出错,因为集合studentList中第二个学生的Name