Не так давно поднял тему обеспечения strong exception safety. Теперь хочу эту тему продолжить маленьким примером.
Представим себе, что есть объект, который должен хранить информацию о неких устройствах. У него есть метод add_device, который нужно вызвать, чтобы добавить информацию о новом устройстве. Этот метод в случае успеха возвращает строковый идентификатор устройства.
Очевидная реализация вышеописанного может выглядеть вот так:
class device_info_manager_t { std::map<compound_device_id_t, device_description_t> m_devices; ... public: ... [[nodiscard]] std::string add_device( const placement_t & place_id, const name_t & name, const device_description_t & info); }; [[nodiscard]] std::string device_info_manager_t::add_device( const placement_t & place_id, const name_t & name, const device_description_t & info) { auto [ins_it, was_inserted] = m_devices.emplace( compound_device_id_t{place_id, name}, info); if(!was_inserted) throw std::runtime_error{"device already added"}; return it->first.to_string(); // (1) } |
К сожалению, эта реализация метода add_device обеспечивает лишь базовую гарантию, а не строгую.
Это потому, что в точке (1), при создании нового экземпляра std::string может выскочить std::bad_alloc. В этом случае никакой утечки не будет, но в объекте останется информация об устройстве, не смотря на то, что add_device завершился неудачно.
Исправить ситуацию очень просто. Например, вот так:
[[nodiscard]] std::string device_info_manager_t::add_device( const placement_t & place_id, const name_t & name, const device_description_t & info) { compound_device_id_t dev_id{place_id, name}; std::string result{dev_id.to_string()}; auto [ins_it, was_inserted] = m_devices.emplace(std::move(dev_id), info); if(!was_inserted) throw std::runtime_error{"device already added"}; return result; } |
Теперь возвращаемое значение конструируется еще до того, как будет модифицирован сам объект. Так что если возвращаемую строку не получится создать, то мы даже не будем модифицировать объект-менеджер. Если же std::bad_alloc вылетит при вызове emplace, то опять же ничего страшного: объект-менеджер не модифицирован, ранее созданная строка-результат будет просто потеряна.
Так что для описанного случая обеспечить strong exception safety не сложно.
Сложно сразу об этом подумать ;)
PS. Даже если исключения не используются (допустим их в каком-то языке нет или религия не позволяет) проблема все равно остается актуальной и для преждевременных return-ов.
Комментариев нет:
Отправить комментарий