|
Программирование >> Включение нужных заголовков
поэтому прежде чем садиться за написание Del eteArray, пожалуйста, прочитайте совет 13. Может быть, он убедит вас в том, что лзше обойтись без DeleteArray. Совет 8. Никогда не создавайте контейнеры, содержащие auto ptr Честно говоря, в книге, посвященной эффективному использованию STL, данный совет не совсем уместен. Контейнеры auto ptr (СОАР, Containers Of Auto Ptr) запрещены, а программа, которая попытается их использовать, не будет компилироваться. Комитет по стандартизации С++ приложил неслыханные усилия в этом направлении. Возможно, мне вообще не стоило бы говорить о контейнерах auto ptr - о них вам расскажет компилятор, причем в самых нелестных выражениях. Однако многие программисты работают на платформах STL, на которых СОАР не запрещены. Более того, многие программисты по-прежнему подвержены иллюзии и видят в СОАР простое, прямолинейное, эффективное средство для борьбы с утечкой ресурсов, часто присущей контейнерам указателей (советы 7 и 33). В результате возникает искушение воспользоваться СОАР, даже если их невозможно создать. Вскоре я объясню, почему СОАР произвели такой переполох, что Комитет по стандартизации предпринял специальные шаги по их запрещению. А пока начнем с первого недостатка, для понимания которого не нужно разбираться в autoptr и вообще в контейнерах: СОАР не переносимы. Да и как может быть иначе? Они запрещены стандартом С++, и наиболее передовые платформы STL уже выполняют это требование. Вероятно, со временем платформы STL, которые сейчас не соответствуют Стандарту, выполнят его требования. Когда это произойдет, программы, использующие СОАР, станут еще менее переносимыми, чем сейчас. Тот, кто заботится о переносимости своих программ, отвергнет СОАР хотя бы по этой причине. Впрочем, не исключено, что переносимость вас не волнует. Если это так, позвольте напомнить об уникальном (а по мнению некоторых - нелепом) смысле операции копирования autoptr. При копировании auto ptr право владения объектом, на который ссылается указатель, переходит к копии, а исходному указателю присваивается NULL. Да, вы не ошиблись: копирование указателя auto ptr приводит к его модификации. auto ptr<Widget> pwKnew Widget): pwl ссылается на Widget auto ptr<Widget> pw2(pwl): pw2 ссылается на объект Widget, принадлежащий pwl: pwl присваивается NULL (таким образом, объект Widget передается от pwl к pw2) pwl = pw2: pwl снова ссылается на Widget: pw2 присваивается NULL Конечно, такое поведение необычно и даже по-своему интересно, но для пользователя STL в первую очередь важно то, что оно приводит к крайне неожиданным последствиям. Рассмотрим внешне безобидный фрагмент, который создает вектор auto ptr<Widget> и сортирует его функцией, сравнивающей значения Widget: bool WidgetAPCompareCconst auto ptr<Widget>& Ihs. const auto ptr<Widget>& rhs) return *lhs < *rhs: Предполагается, что для объектов Widget существует оператор < vector<auto ptr<Widget> > widgets: Создать вектор и заполнить его указателями autojjtr на Widget. Помните, что этот фрагмент не должен компилироваться! sortCwidgets.beginO,widgets.endO. Отсортировать вектор widgetAPCompare): Пока все выглядит вполне разумно, да и с концептуальной точки зрения все действительно разумно - но результат разумным никак не назовешь. Например, в процессе сортировки некоторым указателям auto ptr, хранящимся в Wi dget, может быть присвоено значение NULL. Сортировка вектора приводит к изменению его содержимого! Давайте разберемся, как это происходит. Оказывается, реализация sort часто строится на некой разновидности алгоритма быстрой сортировки. Работа этого алгоритма строится на том, что некоторый элемент контейнера выбирается в качестве опорного , после чего производится рекурсивная сортировка по значениям, большим и меньшим либо равным значению опорного элемента. Реализация такого алгоритма в sort может выглядеть примерно так: tempiate<class RandomAccessIterator, Объявление sort скопировано class Compare> прямо из Стандарта void sortCRandomAccessIterator first, RandomAccessIterator last. Compare comp) typedef описывается ниже typedef typename iterator traits<RandomAccessIterator>::value type ElementType: RandomAccessIterator i; Присвоить i указатель на опорный элемент ElementType pivotValue(*i); Скопировать опорный элемент в локальную временную переменную: см. далее комментарий. Остальная сортировка Если вы не привыкли читать исходные тексты STL, этот фрагмент выглядит жутковато, но в действительности в нем нет ничего страшного. Нетривиально здесь выглядит только запись iterator trai ts<RandoniAccessIterator>:; val ue type, но это всего лишь принятое в STL обозначение типа объекта, на который указывают итераторы, переданные sort. Перед ссылкой iterator traits<RandomAccessIterator>:: value type должен стоять префикс typename, поскольку это имя типа, зависящее от параметра шаблона (в данном слзае RandomAccessIterator), - дополнительная информация приведена на с. 20. Проблемы возникают из-за следующей команды, которая копирует элемент из сортируемого интервала в локальный временный объект: ElementType pivotValue(*i): В данном случае элементом является auto ptr<Wi dget>, поэтому в результате скопированному указателю auto ptr (тому, который хранится в векторе) присваивается NULL. Более того, когда pi votVal ue выходит из области видимости, происходит автоматическое удаление объекта Widget, на который pivotValue ссылается. Итак, после вызова sort содержимое вектора изменяется и по меньшей мере один объект Widget удаляется. Вследствие рекурсивности алгоритма быстрой сортировки существует вероятность того, что сразу нескольким элементам вектора будет присвоено значение NULL и сразу несколько объектов Wi dget будут удалены, поскольку опорный элемент копируется на каждом уровне рекурсии. > Подобные ловушки весьма зловредны, и Комитет по стандартизации постарался, чтобы вы заведомо не попадались в них. Уважайте их труд и никогда не создавайте контейнеры autoptr, даже если ваша платформа STL это позволяет. Впрочем, это вовсе не исключает возможности создания контейнеров умных указателей. Контейнеры умных указателей вполне допустимы. В совете 50 описано, где найти умные указатели, хорошо работающие в контейнерах STL, просто autoptr не относится к их числу. Совет 9. Тщательно выбирайте операцию удаления Допустим, у нас имеется стандартный контейнер STL с, содержащий числа типа int: контейнер<лп1> с; и вы хотите удалить из него все объекты со значением 1963. Как ни странно, способ решения этой задачи зависит от контейнера; универсального решения не существует. Для блоковых контейнеров (vector, deque или string - см. совет 1) оптимальный вариант построен на использовании идиомы erase-remove (совет 32): c.eraseCremoveCc.beginO.c.endO,1963), Идиома erase-remove хорошо c.endO); подходит для удаления элементов с заданным значением из контейнеров vector, string и deque Приведенное решение работает и для контейнеров 1 i st, но как будет показано в совете 44, функция remove контейнера 1 i st работает эффективнее: с.remove(1963); Функция remove хорошо подходит для удаления элементов с заданным значением из списка Стандартные ассоциативные контейнеры (такие как set, multiset, map и multimap) не имеют функции remove с именем remove, а использование алгоритма remove может привести к стиранию элементов контейнера (совет 32) и возможной порче его содержимого. За подробностями обращайтесь к совету 22, где также объясняется, почему вызовы remove для контейнеров map/multimap не компилируются никогда, а для контейнеров set/multiset - компилируются в отдельных случаях.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |