У меня есть класс 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 () перед open ().
РЕДАКТИРОВАТЬ Как указывает Наваз ниже, для реального класса также потребуется конструктор копирования и оператор присваивания.
Кажется, что это достаточно много для того, чтобы вызывать метод close ().Возникают два вопроса:
Это все же проще, чем заставлять пользователей моего класса использовать try / catch?
Есть ли проще?способ достичь того, чего я хочу: предоставить базовую гарантию исключений и убедиться, что close () всегда следует за open ()?