Темы, которые касаются не только тестов, IT и Quizful, вы можете создавать в новом разделе Обсуждения.

Вдобавок, появилась возможность комментировать профиль пользователя на странице профиля.

Надеемся, эти нововведения Вам понравятся.
Знаете ли Вы, что

В разделе "Статьи" можно найти обучающие статьи по информационным технологиям, а также узнать о новостях сервиса Quizful.

Топ контрибуторов
loading
loading
Статистика

Тестов: 127, вопросов: 5126. Пройдено: 54989 / 186427.

STL code snippets (правила использования STL в c++)

head tail Информация о статье
категория
C++
дата15.05.2009
авторanalizer
голосов12

Многие говорят что умеют, на деле умеют не многие

Рекомендуемая литература:

К чему эпиграф?

К тому что многие C++ программисты говорят что умеют пользоваться контейнерами из библиотеки STL. На деле же приходится сталкиваться и с итераторами объявленными вне for и с дополнительным итераторами при удалении элемента из листа. На самом деле есть простые правила которым нужно следовать при обходе стандартных контейнеров:

  1. Не обходите вектор путём перебора его элементов по индексам, вы запросто можете потерять один из добавленных элементов. Используйте итераторы.
  2. Если не собираетесь менять значения элементов или же удалять их - используйте const_iterator. Так вы оставите себе и другим напоминание о том что контейнер нельзя менять.
  3. Никогда не сравнивайте итераторы при помощи операторов "больше" (>) или "меньше" (<), проверяйте их только на равенство (==) и неравенство (!=), стандарт не гарантирует что итератор указывающий на элемент последовательности обязательно должен быть "меньше" итератора элемента который находится в последовательности после данного.
  4. Если разницы нет - предпочитайте префиксный инкремент/декремент постфиксному (это касается не только стандартных контейнеров и итераторов). Префиксные операторы возвращают ссылку на элемент, а постфиксные - копию объекта, причём копирование объекта может занимать порой значительное время.
  5. Если не собираетесь менять количество элементов - объявляйте итераторы начала и конца контейнера в первой секции цикла for. Не надо этими итераторами засорять внешнее пространство имён. Например можно делать так:
    for(std::list<int>::const_iterator iter=lst.begin(), end=lst.end(); iter != end; ++iter)
  6. Если собираетесь менять количество элементов - получайте итератор конца контейнера каждый раз когда проверяется условие продолжения цикла.
  7. При удалении элементов последовательности (sequences) возвращают итератор элемента следующего за удалённым (аналогично для всех контейнеров операция вставки одиночного элемента возвращает итератор вставленного элемента):
    for(std::list<int>::iterator iter=lst.begin(); iter != lst.end(); )
    {
    	if(check_condition(*iter))
    	{
    		iter = lst.erase(iter);
    	}
    	else
    	{
    		++iter;
    	}
    }
  8. По возможности, старайтесь использовать стандартные алгоритмы - это поможет избежать изобретения собственных велосипедов (у которых могут запросто оказаться квадратные колёса). Например, код выше запросто можно заменить этим (feel the difference):
    lst.remove_if(check_condition);
  9. Дополнение к пункту выше - вам не обязательно объявлять функции, которые вы собираетесь передавать в стандартные алгоритмы, в глобальной области видимости. Можно воспользоваться Boost.Lambda или же статьёй приведённой в списке рекомендуемой литературы:
    #include <boost/preprocessor/cat.hpp>
    
    #define LOCAL(R, P) \
      typedef struct { typedef R(*F) P; static R body P {
    
    #define LOCAL_END(name) \
      } } BOOST_PP_CAT(local_func_,__LINE__); \
      const BOOST_PP_CAT(local_func_,__LINE__)::F name = BOOST_PP_CAT(local_func_,__LINE__)::body
    ...
    LOCAL(bool, (int i))
    	return i & 1;
    LOCAL_END(is_odd);
    lst.remove_if(is_odd);
  10. Если вы обходите массив по индексам, индексы начинаются с нуля (самый распространённый случай) и порядок обхода неважен - обходите не от нулевого элемента к последнему, а от последнего к нулевому (сравнение числа с нулём требует меньше времени чем с какой-либо другой константой):
    int a[10];
    for(unsigned i = 10; i--; )
    	...

Я понимаю, эти правила звучат ультимативно, но из своего опыта могу сказать что если вы будете этим правилам следовать, то хуже ваш c++ код точно не станет.

Статья получилась очень короткая, но кто понял о чём правила выше - тот возьмёт на вооружение, кто уже использовал - даст почитать другим.

----

Дмитрий analizer Потапов, апрель 2009

Если Вам понравилась статья, проголосуйте за нее

Голосов: 12  loading...
sgauStudent   clumsy   md6   misha91   alexis112   Sekt104   ligth1987   valyala   Shved90   serj   admin   turanga_leela  
Комментариев: 5
 ksuzev23.04.2010 | 17:09:48
Совет 1:
Дело не только в добавленных элементах. Важно то, что код с проходом контейнера через итераторы не нужно будет переписывать, если вектор поменяют к примеру на список.

Совет 5:
Такую длинную контрукцию лучше в одну строчку не впихивать, лучше использовать компромисный, более читаемый вариант:
typedef std::list<int>::const_iterator ci;
for(ci iter=lst.begin(), end=lst.end(); iter != end; ++iter)

Если же используется компилятор, поддерживающий новый стандарт С++, то можно записать в одну строчк:
for(auto iter=lst.cbegin(), end=lst.cend(); iter != end; ++iter)

Совет 10:
Тут палка о двух концах. Может мы получим выигрышь в сравнении чисел с нулём, а может выигрыш окажется мизерным (это зависит в первую очередь архитектуры процессора).
А вот пенальти за непоследовательный доступ к памяти получим абсолютно точно. Как правило процессор при обращении к памяти делает небольшой префетч, и загружает в кэш нужную ячейку памяти + сколько-то ячеек после неё. Так что если мы будем обращат
ответить
 analizer24.04.2010 | 07:29:17
1. ну да
2. вопрос стиля
3. согласен
ответить
 breusov09.08.2009 | 06:30:35
игры с навороченными template'ами и #define'ами хороши для того, чтобы поразмять мозги или для написания библиотеки, но использование в обычном коде фрагмента похожего на пункт 9, практически полностью исключит возможость исправить его в будущем - будет впадлу опять разбираться))
ответить
 analizer09.08.2009 | 07:53:20
Сниппеты пишутся один раз, и после их первоначальной отладки уже не исправляются.
ответить
 sgauStudent20.07.2009 | 21:03:51
супер статья. Пишите, пожалуйста, больше о буст и стл, потому что они делают плюсы безопаснее.
ответить
Добавить комментарий