开发者

How to handle object destruction in error case vs. non-error case

I have a program that is responsible for reading data, formatting it and creating records, and outputting records to files. The important classes for this discussion are:

  • RecordGenerator - contains the thread that controls the main flow (get data, format, output)
  • FileManager - manages the output files. Records are sent to this class which then puts it in a charging file.
  • OutputFile - abstraction of a file that contains records, has print(), close(), etc. These objects are owned by the FileManager

During a normal process shutdown, the destructors for these classes all get called which causes all remaining records to get flushed out to the current output file and then it gets closed. This ensures we don't lose any data. However, during an error case, we need to shutdown but we don't want to flush and close the file since the data is likely corrupt. Normally what happens is an exception will get thrown which gets caught in the RecordGenerator which then decides if this is a fatal error or not. If it is, it will initiate the application shutdown. It's at this point that the FileManager gets destructed, but needs to know whether there is an error. Likewise, when the FileManager gets destructed, this causes the OutputFile to get destructed which also needs to know whether there is an error.

My first reaction was to just add some public functions that set error flags for these classes, so RecordGenerator could call FileManager::setErrorFlag() which can then call OutputFile::setErrorFlag(). Adding a chain of these seems like a pretty bad smell to me, especially if you consider the object chain could be much longer t开发者_如何学JAVAhan this.

Is there some better way of handling this sort of scenario?


This is a typical problem when people start using RAII the way it's not meant to be used. Destructors should clean resources and revert whatever they are responsible to. They should not commit changes. Typical exception safe C++ code looks like this:

  • allocate resource
  • do something
  • commit changes

For example:

X& X::operator = (const X& x)
{
    X y(x); // allocate
    this->swap(y); // commit
    return *this;
}

void f()
{
    Transaction t(...); // begin transaction
    // operate
    t.commit(); // commit transaction
}

void g()
{
    File f(...); // open file
    // write to file
    f.flush(); // flush the buffers, this may throw but not f.~File()
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜