Хотя другие ответы верны и полезны, я думаю, что настоящая причина проще.
Дизайн iostreams намного старше многих стандартных библиотек и предшествует широкому использованию исключений. Я подозреваю, что для совместимости с существующим кодом использование исключений было сделано необязательным, а не по умолчанию для невозможности открыть файл.
Кроме того, ваш вопрос действительно имеет отношение только к файловым потокам, другие типы стандартных потоков не имеют функций-членов open()
или close()
, поэтому их конструкторы не выбрасывают, если файл не может быть открыт :-)
Для файлов вы можете проверить успешность вызова close()
, чтобы знать, были ли данные записаны на диск, так что это веская причина не делать это в деструкторе, потому что к тому времени, когда объект уничтожен, уже слишком поздно делать с ним что-либо полезное, и вы почти наверняка не захотите генерировать исключение из деструктора. Таким образом, fstreambuf
вызовет close в своем деструкторе, но вы также можете сделать это вручную перед уничтожением, если хотите.
В любом случае, я не согласен с тем, что это не соответствует соглашениям RAII...
Почему разработчики библиотеки предпочли свой подход открытию только в конструкторах, которые вызывают ошибку?
Н.Б. RAII не означает, что вы не можете иметь отдельный элемент open()
в дополнение к конструктору, получающему ресурсы, или вы не можете очистить ресурс перед уничтожением, например. unique_ptr
есть reset()
член.
Кроме того, RAII не означает, что вы должны создавать ошибки при сбое или объект не может находиться в пустом состоянии, например. unique_ptr
может быть создан с нулевым указателем или создан по умолчанию, и поэтому также может ни на что не указывать, поэтому в некоторых случаях вам нужно проверить его перед разыменованием.
Файловые потоки получают ресурс при построении и освобождают его при уничтожении — это, насколько я понимаю, RAII. То, против чего вы возражаете, требует проверки, что пахнет двухэтапной инициализацией, и я согласен, что это немного вонюче. Однако это не делает его не RAII.
В прошлом я решал эту проблему с помощью класса CheckedFstream
, который представляет собой простую оболочку, добавляющую единственную функцию: добавление коснтруктора, если поток не может быть открыт. В С++ 11 это так же просто:
struct CheckedFstream : std::fstream
{
CheckedFstream() = default;
CheckedFstream(std::string const& path, std::ios::openmode m = std::ios::in|std::ios::out)
: fstream(path, m)
{ if (!is_open()) throw std::ios::failure("Could not open " + path); }
};
person
Jonathan Wakely
schedule
02.09.2014
throw_exception
. Можно установить исключения для более поздних операций, но лучше использовать конструктор бросков. - person BЈовић   schedule 03.09.2014