C++RAII来管理对象状态的更改和恢复
C++ RAII to manage change and reversion of object state
我有一个类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:。我不能使用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((方法的明显改进,尽管在实际对象中,我这样做是为了防止在open((之前出现write(((。
EDIT正如Nawaz在下面指出的,一个真正的类还需要一个复制构造函数和赋值运算符。
为了确保调用close((,这似乎是一个很大的样板。出现两个问题:
这比强迫类的用户使用try/catch还简单吗?
有没有一种更简单的方法可以实现我想要的:提供基本的异常保证,并确保close((始终跟在open((后面?
嵌套类opener
应该实现复制语义,因为如果我正确理解您的意图,编译器生成的默认代码会产生不希望的结果。
所以请实现复制构造函数和复制赋值。
或者,您可能希望通过使它们的声明1private
完全禁用复制语义,这与所有标准流类的实现非常相似。我更喜欢这种方法。
1.请注意,您不需要定义它们。仅在private
部分中声明它们就足够了
关闭是否会失败?如果可以的话,那么无论采取什么方法,你都需要格外小心。不过,我认为RAII方法比强制用户进行异常处理/关闭更简单。
foo
的创建和销毁真的如此复杂吗(或者它是全局的吗?(以至于你不能只让它的析构函数调用close而不是使用opener来进行匹配的打开/关闭?
或者,如果这是在实现某种事务语义,我看不到比opener类更简单的方法(但正如其他答案中所指出的,您可能希望禁用opener类的复制和赋值(。
我认为您应该将您的关注点分开:
- 一个类,用于存储贯穿始终的状态
- 一个类,用于处理打开/关闭中的瞬态,它还负责所有"瞬态"操作,如写入
"transient"类将"data"类作为参数(通过引用(,并将在各种方法调用期间更新它。
然后,您可以在瞬态类上使用典型的RAII,并且仍然在整个类中传播状态。
这是使用RAII的经典案例。请使用构造函数和析构函数:如果要再次使用open()
,请实例化一个新的foo
。RAII的思想是实例化表示单个资源的使用。这就是获得资源清理保证的方法。
struct foo {
foo() { open(); }
~foo() { close(); }
void write(const std::string& s);
private: // or public
foo(const foo&);
foo& operator=(const foo&);
};
// ...
{ foo fooa;
fooa.write("hello 0");
} { foo foob;
foob.write("hello 1");
}
您可以向类添加一个标志,如果调用open((,则该标志设置为true,如果调用close((,该标志将设置为false。如果调用open((,则可以检查标志是true还是false,如果需要,则在继续之前关闭。
- 用C++将哈希表写入文件并从文件中恢复
- Constexpr替代了新的放置方式,可以让内存中的对象保持未初始化状态
- 我不断收到 [错误] ID 返回 1 退出状态错误,但看不到问题所在
- OSX MetalKit CVMetalTextureCacheCreateTextureFromImage失败,状态:
- std::future_error:无关联状态
- 如何避免LED在循环状态变化中闪烁?
- 使用c++后,将堆栈恢复到以前的状态
- 无法恢复我的安卓 c++ 游戏的状态(状态>保存状态始终为空)
- 如何在C++中保存和恢复 TensorFlow 图及其状态
- 如何使用CUDA CURAND保存和恢复随机数生成器的状态
- 正在恢复 OpenGL 状态
- 在基于对话框的MFC应用程序中保存和恢复元素的状态
- 如何使系统进入睡眠状态并再次自动(以编程方式)从睡眠状态恢复,而无需任何手动"鼠标单击或键盘点击"
- 支持存储/恢复排序状态的PRNG库
- 如何保存std::mersenne_twister_engine的状态以便以后恢复
- C++RAII来管理对象状态的更改和恢复
- 如何保存-恢复所有opengl状态变量
- 如何恢复绘图后的QPainter状态
- 协程或有状态/可恢复任务与chiscript
- 如何强制DirectX恢复之前的渲染状态?