Совет 4. Вызывайте empty вместо сравнения size() с нулем
Совет 4. Вызывайте empty вместо сравнения size() с нулем
Для произвольного контейнера с следующие две команды фактически эквивалентны:
if (c.size()==0)...
if (c.empty())...
Возникает вопрос — почему же предпочтение отдается одной конструкции, особенно если учесть, что empty обычно реализуется в виде подставляемой (inline) функции, которая просто сравнивает size() с нулем и возвращает результат?
Причина проста: функция empty для всех стандартных контейнеров выполняется с постоянной сложностью, а в некоторых реализациях list вызов size требует линейных затрат времени.
Но почему списки так себя ведут? Почему они не обеспечивают выполнения size с постоянной сложностью? Это объясняется в основном уникальными свойствами функций врезки (splicing). Рассмотрим следующий фрагмент:
list<int> list1;
list<int> list2;
list1.splice( // Переместить все узлы list2
list1.end(),list2, // от первого вхождения 5
find(list2.begin(),list2.end(), 5),// до последнего вхождения 10
find(list2.rbegin().list2.rend(),10).base()// в конец listl
);// Вызов base() рассматривается
// в совете 28
Приведенный фрагмент не работает, если только значение 10 не входит в list2 после 5, но пока не будем обращать на это внимания. Вместо этого зададимся вопросом: сколько элементов окажется в списке list1 после врезки? Разумеется, столько, сколько было до врезки, в сумме с количеством новых элементов. Последняя величина равна количеству элементов в интервале, определяемом вызовами find(list2.begin(),list2.end(), 5) и find(list2.rbegin(),list2.rend(),10).base(). Сколько именно? Чтобы ответить на этот вопрос, нужно перебрать и подсчитать элементы интервала. В этом и заключается проблема.
Допустим, вам поручено реализовать list. Это не просто контейнер, а стандартный контейнер, поэтому заранее известно, что класс будет широко использоваться. Естественно, реализация должна быть как можно более эффективной. Операция определения количества элементов в списке будет часто использоваться клиентами, поэтому вам хотелось бы, чтобы операция size работала с постоянной сложностью. Класс list нужно спроектировать так, чтобы он всегда знал количество содержащихся в нем элементов.
В то же время известно, что из всех стандартных контейнеров только list позволяет осуществлять врезку элементов без копирования данных. Можно предположить, что многие клиенты выбирают list именно из-за эффективности операции врезки. Они знают, что интервальная врезка из одного списка в другой выполняется за постоянное время; вы знаете, что они это знают, и постараетесь не обмануть их надежды на то, что функция splice работает с постоянными затратами времени.
Возникает дилемма. Чтобы операция size выполнялась с постоянной сложностью, каждая функция класса list должна обновлять размеры списков, с которыми она работает. К числу таких функций относится и splice. Но сделать это можно только одним способом — функция должна подсчитать количество вставляемых элементов, а это не позволит обеспечить постоянное время выполнения splice... чего мы, собственно, и пытались добиться. Если отказаться от обновления размеров списков функцией splice, добиться постоянного времени выполнения для splice можно, но тогда с линейной сложностью будет выполняться size — ей придется перебирать всю структуру данных и подсчитывать количество элементов. Как ни старайся, чем-то — size или splice — придется пожертвовать. Одна из этих операций может выполняться с постоянной сложностью, но не обе сразу.
В разных реализациях списков эта проблема решается разными способами в зависимости от того, какую из операций — size или splice — авторы хотят оптимизировать по скорости. При работе с реализацией list, в которой было выбрано постоянное время выполнения splice, лучше вызывать empty вместо size, поскольку empty всегда работает с постоянной скоростью. Впрочем, даже если вы не используете такую реализацию, не исключено, что это произойдет в будущем. Возможно, программа будет адаптирована для другой платформы с другой реализацией STL, или вы перейдете на новую реализацию STL для текущей платформы.
В любом случае вы ничем не рискуете, вызывая empty вместо проверки условия size()=0. Мораль: если вам потребовалось узнать, содержит ли контейнер ноль элементов — вызывайте empty. .
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Page size
Page size Размер страницы базы данных, исчисляется в байтах. Параметр, который устанавливает основополагающее свойство базы данных - размер ее страницы. Все файлы одной базы данных состоят из страниц одинакового размера, который устанавливается при создании базы данных и
EVENT MEMORY SIZE
EVENT MEMORY SIZE Параметры в ibconfig V4_EVENT_MEM_S1ZE 32768ANY_EVENT_MEM_SIZE 32768
DATABASE CACHE SIZE
DATABASE CACHE SIZE Параметры в ibconfig DATABASE_CACHE_PAGES 75
Правило 9: Никогда не вызывайте виртуальные функции в конструкторе или деструкторе
Правило 9: Никогда не вызывайте виртуальные функции в конструкторе или деструкторе Начну с повторения: вы не должны вызывать виртуальные функции во время работы конструкторов или деструкторов, потому что эти вызовы будут делать не то, что вы думаете, и результатами их
Совет 5. Используйте интервальные функции вместо одноэлементных
Совет 5. Используйте интервальные функции вместо одноэлементных Есть два вектора, v1 и v2. Как проще всего заполнить v1 содержимым второй половины v2? Только не надо мучительно размышлять над тем, что считать «половиной» при нечетном количестве элементов в v2. Просто
Совет 20. Определите тип сравнения для ассоциативного контейнера, содержащего указатели
Совет 20. Определите тип сравнения для ассоциативного контейнера, содержащего указатели Предположим, у нас имеется контейнер set, содержащий указатели string*, и мы пытаемся включить в него несколько новых элементов:set<string*> ssp; // ssp = "set of string ptrs"ssp.insert(new string("Anteater"));ssp.insert(new
Совет 21. Следите за тем, чтобы функции сравнения возвращали false в случае равенства
Совет 21. Следите за тем, чтобы функции сравнения возвращали false в случае равенства Сейчас я покажу вам нечто любопытное. Создайте контейнер set с типом сравнения less_equal и вставьте в него число 10:set<int,less_equal<int> > s; // Контейнер s сортируется по критерию "<="s.insert(10); // Вставка
Совет 26. Старайтесь использовать iterator вместо const_iterator, reverse_iterator и const_reverse_iterator
Совет 26. Старайтесь использовать iterator вместо const_iterator, reverse_iterator и const_reverse_iterator Как известно, каждый стандартный контейнер поддерживает четыре типа итераторов. Для контейнера container<T> тип iterator работает как Т* тогда как const_iterator работает как const Т* (также встречается запись
Совет 35. Реализуйте простые сравнения строк без учета регистра символов с использованием mismatch или lexicographical_compare
Совет 35. Реализуйте простые сравнения строк без учета регистра символов с использованием mismatch или lexicographical_compare Один из вопросов, часто задаваемых новичками в STL — «Как в STL сравниваются строки без учета регистра символов?» Простота этого вопроса обманчива. Сравнения
Совет 43. Используйте алгоритмы вместо циклов
Совет 43. Используйте алгоритмы вместо циклов Каждому алгоритму передается по крайней мере одна пара итераторов, определяющих интервал объектов для выполнения некоторой операции. Так, алгоритм min_element находит минимальное значение в интервале, алгоритм accumulate вычисляет
Совет 44. Используйте функции контейнеров вместо одноименных алгоритмов
Совет 44. Используйте функции контейнеров вместо одноименных алгоритмов Некоторые контейнеры содержат функции, имена которых совпадают с именами алгоритмов STL. Так, в ассоциативных контейнерах существуют функции count, find, lower_bound, upper_bound и equal_range, а в контейнере list
Совет 46. Передавайте алгоритмам объекты функций вместо функций
Совет 46. Передавайте алгоритмам объекты функций вместо функций Часто говорят, что повышение уровня абстракции языков высокого уровня приводит к снижению эффективности сгенерированного кода. Александр Степанов, изобретатель STL, однажды разработал небольшой комплекс
Меж нулем и единицей
Меж нулем и единицей 54 года - и все тот же «принцип арифмометра»…Впрочем, грех жаловаться - этот принцип оказался столь гибким в приложениях, что даже принципиально новые идеи вычислительных устройств (например, нейроподобные сети или системы с нечеткой логикой) вначале
Не вызывайте нас, мы вызовем вас
Не вызывайте нас, мы вызовем вас Класс SEQUENTIAL_TABLE дает представление о том, как ОО-технология, используя понятие класса поведения, отвечает на последний оставшийся открытым в лекции 4 вопрос о "Факторизации общих поведений".Особенно интересна возможность определения такой