Многое из того, что говорилось в этом совете по поводу эффективности insert
, относится и к erase
. Интервальная форма erase
также сокращает количество вызовов функций по сравнению с одноэлементной формой. При одноэлементном удалении элементы тоже сдвигаются на одну позицию к своему итоговой позиции, тогда как в интервальном варианте каждый элемент перемещается к итоговой позиции за одну операцию.
Но erase не присущ такой недостаток insert
контейнеров vector
и string
, как многократные выделения памяти (конечно, для erase речь пойдет о многократном освобождении ). Дело в том, что память, занимаемая vector
и string
, автоматически увеличивается для новых элементов, но при уменьшении количества элементов память не освобождается (в совете 17 рассказано о том, как уменьшить затраты освободившейся памяти в vector
и string
).
К числу особенно важных аспектов интервального удаления относится идиома erase-remove, описанная в совете 29.
• Интервальное присваивание. Как упоминалось в самом начале совета, во всех последовательных контейнерах предусмотрена интервальная форма assign
:
void контейнер: :assign(InputIterator begin, InputIterator end);
Итак, мы рассмотрели три веских аргумента в пользу применения интервальных функций вместо их одноэлементных аналогов. Интервальные функции обеспечивают более простую запись, они более четко выражают ваши намерения и обладают более высоким быстродействием. Против этого трудно что-либо возразить.
Совет 6. Остерегайтесь странностей лексического разбора C++
Предположим, у вас имеется файл, в который записаны числа типа int
, и вы хотите скопировать эти числа в контейнер list
. На первый взгляд следующее решение выглядит вполне разумно:
ifstream dataFile("ints.dat");
list data(istream_iterator(dataFile), // Внимание! Эта строка
istream_iterator()); // работает не так, как
// вы предполагали
Идея проста: передать пару istream_iterator
интервальному конструктору list
(совет 5), после чего скопировать числа из файла в список.
Программа будет компилироваться, но во время выполнения она ничего не сделает. Она не прочитает данные из файла. Она даже не создаст список — а все потому, что вторая команда не объявляет список и не вызывает конструктор. Вместо этого она… Произойдет нечто настолько странное, что я даже не рискну прямо сказать об этом, потому что вы мне не поверите. Вместо этого я попробую объяснить суть дела постепенно, шаг за шагом. Надеюсь, вы сидите? Если нет — лучше поищите стул…
Начнем с азов. Следующая команда объявляет функцию f
, которая получает double
и возвращает int
:
int f(double d);
То же самое происходит и в следующей строке. Круглые скобки вокруг имени параметра d
не нужны, поэтому компилятор их игнорирует:
int f(double(d));// То же,- круглые скобки вокруг d игнорируются
Рассмотрим третий вариант объявления той же функции. В нем просто не указано имя параметра:
int f(double);// То же; имя параметра не указано
Вероятно, эти три формы объявления вам знакомы, хотя о возможности заключать имена параметров в скобки известно далеко не всем (до недавнего времени я о ней не знал).
Теперь рассмотрим еще три объявления функции. В первом объявляется функция g
с параметром — указателем на функцию, которая вызывается без параметров и возвращает double
:
int g(double (*pf)()); // Функции g передается указатель на функцию
То же самое можно сформулировать и иначе. Единственное различие заключается в том, что pf
объявляется в синтаксисе без указателей (допустимом как в C, так и в C++):
int g(double pf()); // То же; pf неявно интерпретируется как указатель
Как обычно, имена параметров могут опускаться, поэтому возможен и третий вариант объявления g
без указания имени pf
:
int g(double());// То же: имя параметра не указано
Обратите внимание на различия между круглыми скобками вокруг имени параметра (например, параметра d
во втором объявлении f
) и стоящими отдельно (как в этом примере). Круглые скобки, в которые заключено имя параметра, игнорируются, а круглые скобки, стоящие отдельно, обозначают присутствие списка параметров; они сообщают о присутствии параметра, который является указателем на функцию.
После небольшой разминки с объявлениями f
и g
мы возвращаемся к фрагменту, с которого начинается этот совет. Ниже он приводится снова:
Читать дальше