- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
为什么 C++ 标准库流使用与对象生命周期分离的 open()
/close()
语义?在销毁时关闭可能在技术上仍会使类成为 RAII,但获取/释放独立性会在句柄可以指向任何内容但仍需要运行时检查才能捕获的范围中留下漏洞。
为什么库设计者选择他们的方法而不是只在引发失败的构造函数中打开?
void foo() {
std::ofstream ofs;
ofs << "Can't do this!\n"; // XXX
ofs.open("foo.txt");
// Safe access requires explicit checking after open().
if (ofs) {
// Other calls still need checks but must be shielded by an initial one.
}
ofs.close();
ofs << "Whoops!\n"; // XXX
}
// This approach would seem better IMO:
void bar() {
std_raii::ofstream ofs("foo.txt"); // throw on failure and catch wherever
// do whatever, then close ofs on destruction ...
}
这个问题的更好措辞可能是为什么访问未打开的 fstream
是值得的。通过句柄生命周期控制打开文件的持续时间在我看来根本不是负担,实际上是一种安全优势。
最佳答案
虽然其他答案都是有效且有用的,但我认为真正的原因更简单。
iostreams 的设计比许多标准库的设计要古老得多,并且早于异常的广泛使用。我怀疑为了与现有代码兼容,异常的使用是可选的,而不是打开文件失败的默认设置。
另外,您的问题仅与文件流真正相关,其他类型的标准流没有 open()
或 close()
成员函数,所以如果无法打开文件,它们的构造函数不会抛出:-)
对于文件,您可能需要检查 close()
调用是否成功,以便知道数据是否已写入磁盘,这是一个很好的理由不在析构函数中执行此操作,因为当对象被销毁时,对它做任何有用的事情都为时已晚,而且您几乎肯定不想从析构函数中抛出异常。因此 fstreambuf
会在其析构函数中调用 close,但如果您愿意,您也可以在销毁之前手动执行此操作。
无论如何,我不同意它不遵循 RAII 约定...
Why did the library designers choose their approach over having opening only in constructors that throw on a failure?
注意RAII 并不意味着除了获取资源的构造函数之外,您不能拥有单独的 open()
成员,或者您不能> 在销毁之前清理资源,例如unique_ptr
有一个 reset()
成员。
此外,RAII 并不意味着您必须抛出失败,或者对象不能处于空状态,例如unique_ptr
可以用空指针构造或默认构造,因此也可以不指向任何内容,因此在某些情况下,您需要在取消引用之前对其进行检查。
文件流在构造时获取资源并在销毁时释放它 - 就我而言,这就是 RAII。你反对的是需要检查,这有两阶段初始化的味道,我同意这有点臭。但这并不意味着它不是 RAII。
过去我用 CheckedFstream
类解决了这个问题,这是一个简单的包装器,添加了一个功能:如果无法打开流,则抛出 cosntructor。在 C++11 中就这么简单:
struct CheckedFstream : std::fstream
{
CheckedFstream() = default;
CheckedFstream(std::string const& path, std::ios::openmode m = std::ios::in|std::ios::out)
: fstream(path, m)
{ if (!is_open()) throw std::ios::failure("Could not open " + path); }
};
关于c++ - 为什么 C++ 标准文件流不更紧密地遵循 RAII 约定?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25624774/
Wikipedia (和其他一些来源)指出: In RAII, holding a resource is tied to object lifetime: resource allocation (
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题? Update the question所以它是on-topic对于堆栈溢出。 9年前关闭。 Improve this que
我过去主要认为 RAII 是关于使用对象生命周期来避免资源泄漏,这在实践中对我很有用。但是我最近讨论了什么是 RAII 模式,什么不是,这让我在网上搜索了更多的定义和评论,结果增加了更多的困惑而不是清
我编写此代码是为了递归遍历文件夹树并列出文件大小(以字节为单位)。 由于我使用的是 winapi 并且有一个 Handle应该打开和关闭,我应该在这段代码上实现 RAII,问题是在线论坛上给出的例子(
我有一个 foo 类。对 foo 的操作需要调用 foo::open()、一些 foo::write(),并且必须以 foo::close() 调用结束: #include class foo {
问题 如何在 RAII 作用域内初始化一个对象,并在该作用域外使用它? 背景 我有一个全局锁,可以用 lock() 和 unlock() 调用。 我有一个类型,LockedObject,只有在全局锁锁
在性能方面,是否有更好的方法来做到这一点? 例如,创建一个名为 arraydata 的类/结构,它分配一些对齐的内存供使用(通过 .dataPtr 提供的指针): class arraydata//t
我是 C++ 的新手,也是使用 RAII 删除分配的内存的新手。我编写这段代码作为自动分配和稍后删除字符数组所需的示例。我知道那里有一个字符串类,但我想我会先从旧的东西开始。这个样本看起来正确吗?这是
(我修改了原来的问题更有意义) 关于 return 语句,Raii 对象是否在 return 语句之前/之后/之间销毁? 例如 size_t advance() { boost::lock_g
我刚刚了解了 RAII。据我了解,RAII 的定义就是它的名称本身。例如,(假设 A 是一个类),考虑下面的函数: void foo(){ A a; a.init(); // Do
由于 RAII,C++ 中没有 finally block 。现在,如果我有一个指针对象,并且其中一个方法发生异常,将如何删除该对象?。这是我编写的示例代码。 class A { public:
如果有一个类: class classA { public: int z = 5; }; 我在编写 classA Aobject 时理解 RAII,但是如果我想声明一个全局指针,我该怎么办?
我想围绕使用 malloc/free 的 C 库编写一个库包装类 (LibWrap)。为此,我想使用 C++ 的 RAII 来分配和释放内存。我使用 lib_address 作为我将从图书馆收到的随机
直到我关于 RAII 和堆栈展开的“相互缠绕”(因为缺少更好的词)的概念是/是相当(如果不是完全)错误的。我的理解是,使用 RAII 可以防止任何/所有资源泄漏——甚至可能由未处理的异常引起的泄漏。
我创建了一个抽象类,然后创建了继承这个抽象类的子类。 class A{ public: virtual A* clone() const = 0; virtual A* creat
#define _CRTDBG_MAP_ALLOC #include #include struct A { A(char* p) : p(p) {} ~A
Scheme 中是否有实现资源获取即初始化的方案? 我知道 RAII 在 GC-ed 语言中效果不佳(因为我们不知道对象何时被销毁)。然而,Scheme 有一些不错的东西,比如延续、动态风和闭包——有
为了安全地使用 C++,我想封装几个 C 函数。有一个 C 函数,它接受指向数组的原始指针及其大小,比如 - void function(char* bufferToFill, size_t bufs
上次我看到一些视频,其中 Stroustrup 先生谈论 C++ 中的 RAII。我想了解更多并找到此页面: https://www.securecoding.cert.org/confluence/
在我的代码中我使用 HANDLE来自 windows.h .它们的用法如下 HANDLE h; if (!openHandleToSomething(arg1, arg2, &h)) { th
我是一名优秀的程序员,十分优秀!