Имеется такой простой интерфейсик:
struct text_out_t
{
public:
virtual void put(char c) const = 0;
virtual ~text_out_t() {}
};
//пользователь как-то определеляет свои операторы <<
const text_out_t &operator <<(const text_out_t &to, int i)
{
to.put('0' + i%10); //только первую цифру, но это просто пример.
return to;
}
//другой пользователь как-то определеляет своих потомков text_out_t
struct file_text_out_t: public text_out_t
{
FILE *m_out_file;
file_text_out_t(FILE *in_in_file)
:m_out_file(in_in_file)
{
}
void put(char c) const
{
if (-1 == putc(c, m_out_file))
throw std::runtime_error("write operation failed");
}
};
//да, возврат по значению, так и должно быть.
file_text_out_t text_out(FILE *f)
{
return file_text_out_t(f);
}
//третий пользователь это всё как-то юзает.
int main()
{
FILE *file = stdout;
text_out(file) << 1 << 2 << 3;
}
Теперь предположим, что профайлер показал, что виртуальная функция на каждый символ — это слишком жирно.
Можно делать буферизацию, типа такой:
struct text_out_t
{
protected:
mutable char buf[16];
mutable int top;
virtual void put_batch() const = 0;
text_out_t()
:top(0)
{
}
public:
// теперь inline, а не виртуальтная.
void put(char c) const
{
if (top == 16)
put_batch();
buf[top++] = c;
}
virtual ~text_out_t() {}
};
//другой пользователь как-то определеляет своих потомков text_out_t
struct file_text_out_t: public text_out_t
{
FILE *m_out_file;
file_text_out_t(FILE *in_in_file)
:m_out_file(in_in_file)
{
}
virtual void put_batch() const
{
if (text_out_t::top > fwrite(text_out_t::buf, 1, text_out_t::top, m_out_file))
throw std::runtime_error("write operation failed");
text_out_t::top = 0;
}
//!!!!!! Вот здесь и проблема! исключение в деструкторе !
~file_text_out_t()
{
try
{
put_batch();
}
catch (...) //ошибка записи в файл.
{
//и что?
}
}
};
Исключение в деструкторе — зло. (Подробности у Саттера/Страуструпа).
Заставлять пользователя писать что-то вроде
text_out(file) << 1 << 2 << 3 <<flush;
(где исключения будет бросаться не в деструкторе, а в последнем operator <<flush) нельзя. Бесмыссленно, неудобно, будут забывать, и т.д.
проверять uncaugh_exception в деструкторе опять же бесмысленно, см. Саттера. Это прямой путь к memory leaks.
Игнорировать ошибки вывода и исключения нельзя.
ЧТО ЖЕ ДЕЛАТЬ?
PS. на const/mutable внимания не обращайте, это просто что бы кратко набросать конкретно эту проблему и что бы все компилировалось.