C++ RAII 管理对象状态的改变和恢复

标签 c++ raii

我有一个 foo 类。对 foo 的操作需要调用 foo::open()、一些 foo::write(),并且必须以 foo::close() 调用结束:

#include <iostream>

class foo
{
public:
    foo()
    {
        std::cout << "foo::foo()" << std::endl;
    }
    ~foo()
    {
        std::cout << "foo::~foo()" << std::endl;
    }    
    void open()
    {
        std::cout << "foo::open()" << std::endl;
    }
    void close()
    {
        std::cout << "foo::close()" << std::endl;
    }
    void write(const std::string& s)
    {
        std::cout << "foo::write(" << s << ")" << std::endl;
    }    
private:    
    // state that must be retained for entire lifetime of object
};

static void useFoo(foo& my_foo)
{
    my_foo.open();
    my_foo.write("string1");
    my_foo.write("string2");
    my_foo.close();
}

int main(  int argc, char* argv[] )
{
    foo my_foo;
    useFoo(my_foo);
    useFoo(my_foo);
}

正如预期的那样,这将输出以下内容:

foo::foo()
foo::open()
foo::write(string1)
foo::write(string2)
foo::close()
foo::open()
foo::write(string1)
foo::write(string2)
foo::close()
foo::~foo()

我想为我的类 foo 的用户提供一种方法,确保他们不会忘记调用 foo::close(),并确保在发生异常时调用 foo::close()。我不能使用 foo 的析构函数,因为 foo 必须在 foo::close() 之后继续存在,为下一个 foo::open() 做好准备。

我想到了这个 RAII 实现:

#include <iostream>

class foo
{
public:
    class opener
    {
    public:
        explicit opener(foo& my_foo):foo_(my_foo)
        {
            foo_.open();
        };
        ~opener()
        {
            foo_.close();
        };    
    private:
        foo& foo_;
    };    
    foo()
    {
        std::cout << "foo::foo()" << std::endl;
    }
    ~foo()
    {
        std::cout << "foo::~foo()" << std::endl;
    }    
    void open()
    {
        std::cout << "foo::open()" << std::endl;
    }
    void close()
    {
        std::cout << "foo::close()" << std::endl;
    }
    void write(const std::string& s)
    {
        std::cout << "foo::write(" << s << ")" << std::endl;
    } 
    opener get_opener()
    {
        return(opener(*this));
    }   
private:
    // state that must be retained for entire lifetime of object    
};


static void useFoo(foo& my_foo)
{
    foo::opener my_foo_opener = my_foo.get_opener();
    my_foo.write("string1");
    my_foo.write("string2");
}

int main(  int argc, char* argv[] )
{
    foo my_foo;
    useFoo(my_foo);
    useFoo(my_foo);
}

为简单起见,我没有包括让 foo::opener 类公开 foo::write() 方法的明显改进,尽管在真实对象中我会这样做以防止 write() 之前成为可能一个开放()。

编辑 正如 Nawaz 在下面指出的那样,一个真正的类还需要一个复制构造函数和赋值运算符。

为了确保调用 close(),这似乎有很多样板文件。出现两个问题:

  1. 这是否比强制我类(class)的用户使用 try/catch 更简单?

  2. 有没有更简单的方法来实现我想要的:提供基本的异常保证并确保 close() 始终跟在 open() 之后?

最佳答案

嵌套类 opener 应该实现复制语义,因为如果我正确理解您的意图,编译器生成的默认代码会产生不良结果。

所以请实现复制构造函数和复制赋值。

或者,您可能希望通过声明1 private 来完全禁用复制语义,很像implementation of all standard stream classes .我更喜欢这种方法。

1。请注意,您不需要定义它们。只需在 private 部分声明它们就足够了。

关于C++ RAII 管理对象状态的改变和恢复,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7662789/

相关文章:

c++ - C++ 中 undefined reference 错误

c++ - 使用 g++ 将多个源文件编译成一个 .o

c++ - C++ 17 可以处理嵌套的可变参数模板吗?

c++ - 如何设计具有强异常保证的函数?

c++11 - 移动构造函数和 const 成员变量

c++ - 使用带有自定义删除器的 shared_ptr 使 HANDLE RAII 兼容

c++ - 为什么 gcc 会立即销毁我的对象,尽管它的范围? (我如何让它不那样做?)

c++ - 日期时间到 UTC

c++ - 我无法从 QMetaObject 获取方法,方法偏移量和计数相等

C++:释放动态数组(结构成员)和指向该结构的指针的方法