Здравствуйте, Николай Ивченков, Вы писали:
НИ>... Забыть вызвать close всё-таки можно, и во время штатной работы программы это никак не проявится. Поэтому, вероятно, выбранную стратегию следует указывать при создании объекта, а в деструкторе проверять, был ли вызван close. Если вызова close не было, но явно такое намерение не было зафиксировано, то деструктор должен выкинуть assertion failed или сообщить о внутренней ошибке в программе каким-то иным способом. Т.е. надо что-то вроде:
НИ>enum ClosingPolicy { close_explicitly, close_soever };
НИ>....
НИ>X::X(ClosingPolicy closing_policy = close_explicitly) :
НИ> m_closing_policy(closing_policy) {}
НИ>void X::close()
НИ>{
НИ> if (closed)
НИ> return;
НИ> if (!api::close(....))
НИ> throw some_exception();
НИ> assert(closed);
НИ>}
НИ>X::~X()
НИ>{
НИ> if (!closed)
НИ> {
НИ> assert(m_closing_policy != close_explicitly)
НИ> api::close(....);
НИ> }
НИ>}
Хороший вариант. Не соглашусь только с заменой assert'a в X::close на 'if (closed)return'. Повторный вызов — такая же логическая ошибка как и отсутствие вызова. Это не оговоривалось, но assert там присутствовал именно из этих соображений.
При некоторых условиях можно пойти дальше — если api::close возвращает несколько категорий ошибок ('close' как аналогия здесь уже мало подходит, хотя...), то в конструкторе можно указать, какие коды возврата необходимо транслировать в исключения. Если указана генерация хотя бы одного исключения (если дать возможность указывать несколько типов), то проверка в деструкторе на явный вызов close должна выполняться, иначе (игнорирование всех ошибок) ошибочным должен считаться явный вызов close, т.к. в этом случае вызов не приведет к исключению, тогда как основной его смысл — именно детектирование исключений.