Стандарты программирования на С++. 101 правило и рекомендация | страница 29
Симметрия конструктор/деструктор, обеспечиваемая языком С++, воспроизводит симметрию, присущую парам функций захвата/освобождения ресурса, таким как >fopen
/>fclose
, >lock
/>unlock
и >new
/>delete
. Это делает стековые объекты (или объекты со счетчиком ссылок), в конструкторе которых происходит захват ресурса (а в деструкторе его освобождение), превосходным инструментом для автоматизации управления ресурсами.
Автоматизация легко реализуема, элегантна, недорога и по сути безопасна в плане ошибок. Если вы не будете ею пользоваться, то обречете себя на нетривиальную и кропотливую ручную работу по "спариванию" вызовов захвата и освобождения ресурсов, включающую отслеживание всех ветвлений и исключений. Это совершенно неприемлемый путь для С++, который предоставляет возможность автоматизации этой работы при помощи простой в использовании идиомы RAII.
Когда вы имеете дело с ресурсом, который требует спаривания вызовов функций захвата/освобождения, инкапсулируйте этот ресурс в объект, который выполнит эту работу за вас и освободит ресурс в своем деструкторе. Например, вместо непосредственного вызова пары функций (не членов) >OpenPort
/>ClosePort
можно поступить иначе:
>class Port {
>public:
> Port(const string& destination); // Вызов OpenPort
> ~Port(); // вызов ClosePort
> // Порты обычно не клонируются, так что запрещаем
> // копирование и присваивание
>};
>void DoSomething() {
> Port port1("server1:80");
> // ...
>} // Забыть закрыть порт нельзя - он будет закрыт
> // автоматически при выходе из области видимости
>shared_ptr
> // автоматически, когда будет уничтожен последний
> // ссылающийся на него объект shared_ptr
Вы можете также использовать библиотеки, которые реализуют соответствующий шаблон проектирования (см. [Alexandrescu00c]).
При реализации идиомы RAII следует особо тщательно подходить к вопросу о копирующем конструкторе и присваивании (см. рекомендацию 49): обычно генерируемые компилятором версии этих функций не подходят. Если копирование лишено смысла, копирующий конструктор и оператор присваивания можно явным образом запретить, делая их закрытыми членами и не определяя (см. рекомендацию 53). В противном случае копирующий конструктор дублирует ресурс или использует счетчик ссылок на него, и то же делает и оператор присваивания, при необходимости освободив ресурс, которым объект владел до присваивания. Классической ошибкой является освобождение старого ресурса до того, как успешно дублирован новый (см. рекомендацию 71).