Читаем Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ полностью

Решение о том, когда нужно предоставить явное преобразование RAII-объекта к управляемому им ресурсу (посредством функции get), а когда – неявное, зависит от конкретной задачи, для решения которой был спроектирован класс, и условий его применения. Похоже, что лучшее решение – следовать советам правила 18, а именно: делать интерфейсы простыми для правильного применения и трудными – для неправильного. Часто явное преобразование типа функции get – более предпочтительный вариант, поскольку минимизирует шанс получить нежелательное преобразование типов. Однако иногда естественность применения неявного преобразования поможет сделать ваш код чище.

Может показаться, что функции, обеспечивающие доступ к управляемым ресурсам, противоречат принципам инкапсуляции. Верно, но в данном случае это не беда. Дело в том, что RAII-классы существуют не для того, чтобы что-то инкапсулировать. Их назначение – гарантировать, что определенное действие (а именно освобождение ресурса) обязательно произойдет. При желании инкапсуляцию ресурса можно реализовать поверх основной функциональности, но это не является необходимым. Более того, некоторые RAII-классы комбинируют истинную инкапсуляцию реализации с отказом от нее в отношении управляемого ресурса. Например, tr1::shared_ptr инкапсулирует подсчет ссылок, но предоставляет простой доступ к управляемому им указателю. Как и большинство хорошо спроектированных классов, он скрывает то, что клиенту не нужно видеть, но обеспечивает доступ к тому, что клиенту необходимо.

Что следует помнить

• Программные интерфейсы (API) часто требуют прямого обращения к ресурсам. Именно поэтому каждый RAII-класс должен предоставлять возможность получения доступа к ресурсу, которым он управляет.

• Доступ может быть обеспечен посредством явного либо неявного преобразования. Вообще говоря, явное преобразование безопаснее, но неявное более удобно для пользователей.

<p>Правило 16: Используйте одинаковые формы new и delete</p>

Что неправильно в следующем фрагменте?

std::string *stringArray = new std::string[100];

...

delete stringArray;

На первый взгляд, все в полном порядке – использованию new соответствует применение delete, но кое-что здесь совершенно неверно. Поведение программы непредсказуемо. По меньшей мере, 99 из 100 объектов string, на которые указывает stringArray, вероятно, не будут корректно уничтожены, потому что их деструкторы, скорее всего, так и не вызваны.

При использовании выражения new (когда объект создается динамически путем вызова оператора new) происходят два события. Во-первых, выделяется память (посредством функции operator new, см. правила 49 и 51). Во-вторых, для этой памяти вызывается один или несколько конструкторов. При вызове delete также происходят два события: вызывается один или несколько деструкторов, а затем память возвращается системе (посредством функции operator delete, см. правило 51). Важный вопрос, возникающий в связи с использованием delete, заключается в следующем: сколько объектов следует удалить из памяти? Ответ на него и определяет, сколько деструкторов нужно будет вызвать.

В действительности вопрос гораздо проще: является ли удаляемый указатель указателем на один объект или на массив объектов? Это критичный вопрос, поскольку схема распределения памяти для отдельных объектов существенно отличается от схемы выделения памяти для массивов. В частности, при выделении памяти для массива обычно запоминается его размер, чтобы оператор delete знал, сколько деструкторов вызывать. В памяти, выделенной для отдельного объекта, такая информация не хранится. Различные схемы распределения памяти изображены на рисунке ниже (n – размер массива):

Конечно, это только пример. От компилятора не требуется реализовывать схему именно таким образом, хотя многие так и делают.

Когда вы используете оператор delete для указателя, как он может узнать, что где-то имеется информация о размере массива? Только от вас. Если после delete стоят квадратные скобки, то предполагается, что указатель указывает на массив. В противном случае компилятор считает, что это указатель на отдельный объект:

std::string *stringPtr1 = new std::string;

std::string *stringPtr2 = new std::string[100];

...

delete stringPtr1;

delete[]stringPtr2;

Что произойдет, если использовать форму «[]» с stringPtr1? Результат не определен, но вряд ли он будет приятным. В предположении, что память организована, как в приведенной выше схеме, delete сначала прочитает размер массива, а затем будет вызывать деструкторы, не обращая внимания на тот факт, что память, с которой он работает, не только не является массивом, но даже не содержит объектов того типа, для которых должны быть вызваны деструкторы.

Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных