Russian Qt Forum
Апрель 27, 2024, 20:50 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3 4   Вниз
  Печать  
Автор Тема: Итераторы в последовательных контейнерах  (Прочитано 23404 раз)
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« : Июль 18, 2014, 14:58 »

Всем доброго времени суток!
Последнее время я больше нацелен не только на результат выполнения определённых операций, но и на скорость их выполнения.
В связи с этим, меня стал интересовать вопрос использования итераторов.
Использование итераторов в ассоциативных контейнерах для меня выглядит как способ быстрого прохода по значениям контейнера.

В последовательных же контейнерах у меня непонятки.
Меня интересует проход по значениям. Раньше я без зазрения совести использовал конструкцию
Код:
for (int i = 0; i < vec.size(); i++)   // или для каждого третьего элемента i += 3
{
    ...
    vec.at(i);
    ...
}
Сточки зрения читабельности для меня этот вариант был удобен.
Сейчас же меня заинтересовала работа с итераторами, но я плохо себе представляю для чего они нужны в последовательных контейнерах, если существует указатель на начало массива данных. В интернете я нашёл, что итератор позволяет модифицировать объекты контейнера без влияния на сам итератор. Но, ведь при модифицировании контейнера можно поменять и указатель...
Объясните, пожалуйста, тёмному человеку преимущества итераторов.
Также интересует скорость работы с итераторами и с указателями.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #1 : Июль 18, 2014, 15:26 »

Не так давно был уже знатный срач на эту тему: http://www.prog.org.ru/topic_24541_0.html Улыбающийся
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #2 : Июль 18, 2014, 15:45 »

Не так давно был уже знатный срач на эту тему: http://www.prog.org.ru/topic_24541_0.html Улыбающийся

Хохохо!  Смеющийся и как же я это не нашёл!
Спасибо!
« Последнее редактирование: Июль 18, 2014, 16:02 от __Heaven__ » Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #3 : Июль 18, 2014, 22:13 »

В целом, итераторы - просто паттерн "универсального" обхода любых контейнеров.

Суть паттерна: механизмы могут перебирать содержимое контейнера ничего не зная ни о его элементах, ни о нюансах работы контейнера.

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

http://ideone.com/4wyh7u

Код:
#include <iostream>     // std::cout
#include <iterator>     // std::ostream_iterator
#include <algorithm>    // std::generate, std::remove
#include <vector>       // std::vector
#include <ctime>        // std::time
#include <cstdlib>      // std::rand, std::srand

using namespace std;

int main()
{
    //--- создаем и заполняем контейнер случайными данными   
    std::vector<int> data(5);
    std::generate(data.begin(), data.end(), [](){ return (std::rand()%100);}  );
   
    //--- выводим в консоль содержимое контейнера
    std::cout<<" ---1---\n";
    std::copy( data.begin(), data.end(), std::ostream_iterator<int>(std::cout, ", ") );
    std::cout<<'\n';

std::cout<<" ---2---\n";
    for (size_t i = 0, end = data.size() ; i!=end; ++i)
        cout << data[i] << ", ";
    std::cout<<'\n';
   
    std::cout<<" ---3---\n";
    for (auto i = data.begin(), end = data.end(); i!=end ; ++i)
        cout<< *i << ", ";
    std::cout<<'\n';


return 0;
}
Как видите в простых случаях использование итератора принципиально не отличается от индекса.
и как вы видите, stl-алгоритмы используют итераторы, что бы выполнять операции над любыми контейнерами независимо от их устройства.

Есть области, где паттерн "итератор" значительно облегчает жизнь.

Вот например: нужно пройтись по файловой системе, перебирая файлы и папки:

Код:
#include <iostream>
using namespace std;

#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;

int main()
{
    cout<<"WELLCOME TO EXAMPLE APPLICATION!\n";
    fs::path p = fs::current_path();

    std::vector< fs::path > files;
    std::copy( fs::directory_iterator(p), fs::directory_iterator(), std::back_inserter(files) );
    std::copy( files.begin(), files.end(), std::ostream_iterator< fs::path > (std::cout, "\n") );

}

Сначала положит в files список всех файлов и каталогов, а затем выведет в консоль.

Без итераторов данная процедура заняла бы значительно большее количество кода.


Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #4 : Июль 19, 2014, 15:10 »

Мда…
Пойду ка я книжку умную почитаю, ибо ничего я не знаю о лямбда функциях, а пример с фс вообще не понял…
Спасибо за ответ.
Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #5 : Июль 19, 2014, 23:18 »

пример с фс вообще не понял…

Перебираем содержимое каталога программы, и выводим его на экран.

Вот рабочий пример в действии:
http://rextester.com/HUV66703

Тоже самое, но без использования алгоритмов stl:

http://rextester.com/PNAGF68674

Код:
#include <iostream>
using namespace std;

#include <boost/filesystem.hpp>

//псевдоним namespace
//используется для сокращения писанины
namespace fs = boost::filesystem;

int main()
{
    cout<<"WELLCOME TO EXAMPLE APPLICATION!\n";

    //каталог, где была запущена программа   
    fs::path p = fs::current_path();
    cout<<"current path: "<< p<<endl;

    //выводим содержимое этого каталога
    //если встречаем каталог - пишим, что каталог
    //если встречаем обычный файл - пишим, что файл
    cout<< "current directory contains:\n";
    for( fs::directory_iterator it(p), end;  it!=end; ++it  )
        cout << (fs::is_directory(*it)? "directory: " : "file: " )<< *it <<endl;

    //при этом пустой fs::directory_iterator означает конец каталога
    //именно ему равен созданный по дефолту итератор end
   
    //а само содержимое по итератору может быть либо файлом, либо каталогом, либо ссылкой, либо ...
    //в зависимости от ОС и её причуд
   
    //итератор позволяет единообразно итерироваться по элементам файловой системы независимо от того, чем именно являются эти элементы
    //и при необходимости - узнать подробности о природе элемента, на который указывает итератор
}
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #6 : Июль 20, 2014, 09:08 »

Я проверил по скорости исполнения оба кода и получил, что второй, без использования stl алгоритмов, работает в 25 раз быстрее, хоть выводит информации больше.

Можно ли основываясь на этом сделать вывод, что использование stl алгоритмов пагубно влияет на скорость исполнения?
Записан
Bepec
Гость
« Ответ #7 : Июль 20, 2014, 09:28 »

Потому что в первом имеет место копирование Улыбающийся Как известно, это самая нехорошая часть в таких вычислениях Улыбающийся А во втором варианте успешно без этого обходятся Веселый
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #8 : Июль 20, 2014, 09:31 »

Я проверил по скорости исполнения оба кода и получил, что второй, без использования stl алгоритмов, работает в 25 раз быстрее, хоть выводит информации больше.
Вы не забыли, что первый вариант запихивает пути в vector, который с этим справляется хуже всего.

Можно ли основываясь на этом сделать вывод, что использование stl алгоритмов пагубно влияет на скорость исполнения?
Нет. Примеры на столько не равнозначные, что их даже сравнивать нельзя.

« Последнее редактирование: Июль 20, 2014, 09:33 от Old » Записан
Bepec
Гость
« Ответ #9 : Июль 20, 2014, 09:33 »

Чтобы сравнить нужно взять и сравнивать перебор. Т.е. циклы обработки, без подготовки данных.
Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #10 : Июль 20, 2014, 10:21 »

Я проверил по скорости исполнения оба кода и получил, что второй, без использования stl алгоритмов, работает в 25 раз быстрее, хоть выводит информации больше.

Можно ли основываясь на этом сделать вывод, что использование stl алгоритмов пагубно влияет на скорость исполнения?

Нельзя.

В первом примере в вектор помещаются все повстречавшиеся fs::path, что ко всему прочему приводит к постоянному реалоку вектора.И только потом содержимое вектора выводится в консоль.

Во втором - только перебор файловых объектов в цикле и вывод на экран.




« Последнее редактирование: Июль 20, 2014, 10:23 от _Bers » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Июль 22, 2014, 13:03 »

Вот рабочий пример в действии:
http://rextester.com/HUV66703

Тоже самое, но без использования алгоритмов stl:

http://rextester.com/PNAGF68674
Ну второй как-то "не сильно хуже" и, на мой взгляд, гибче. Понадобится напр обновлять индикатор и/или проверить отмену (юзверь нажал cancel) - и как это встроить в std::copy? А при незатейливом цикле этой проблемы нет. Вообще короткий, элегантный текст обоих примеров достигнут за счет использования мощностей буста, силу итераторов он не демонстрирует  Улыбающийся
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #12 : Июль 22, 2014, 13:30 »

Вообще короткий, элегантный текст обоих примеров достигнут за счет использования мощностей буста
Даааа, мощности там задействованы серьезные. Улыбающийся

силу итераторов он не демонстрирует  Улыбающийся
Потому что вы от них очень многого ждете. Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #13 : Июль 22, 2014, 14:09 »

Даааа, мощности там задействованы серьезные. Улыбающийся
Очень даже серьезные. Я так вижу Вам не приходилось это в нативняке исполнять - выглядит примерно так (начало)
Код
C++ (Qt)
FSIterator iterator;
OSStatus error = FSOpenIterator(&ref, kFSIterateFlat, &iterator);
if (error) return false;
 
ItemCount maxCount = 10;
FSSpec FS[10];
 
while (true) {
error = FSGetCatalogInfoBulk(
iterator,
10, // ItemCount maximumObjects,
&maxCount, // ItemCount * actualObjects,
0, // Boolean * containerChanged,
kFSCatInfoNone, // FSCatalogInfoBitmap whichInfo,
0, // FSCatalogInfo * catalogInfos,
0, // FSRef * refs,
FS, // FSSpec * specs,
0 // HFSUniStr255 * names
);
if (error && error != errFSNoMoreItems) break;
Boolean aliasFileFlag, folderFlag;
 
for (int i = 0; i < maxCount; ++i) {
 OSErr err2 = IsAliasFile(FS + i, &aliasFileFlag, &folderFlag);
...
Главное что вот эту всю грязную работу за меня сделали - и на всех платформах, и за это я даже согласен с итераторами  Улыбающийся
« Последнее редактирование: Июль 22, 2014, 14:10 от Igors » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #14 : Июль 22, 2014, 15:03 »

Очень даже серьезные.
Да, вот это серьезность. Улыбающийся

Я так вижу Вам не приходилось это в нативняке исполнять - выглядит примерно так (начало)
Скажу вам по секрету, но в нативняке для этого нужно использовать:
Код
C++ (Qt)
   DIR *dir;
   struct dirent *entry;
 
   dir = opendir("/");
   if (!dir) {
       perror("diropen");
       exit(1);
   };
 
   while ( (entry = readdir(dir)) != NULL) {
       printf("%d - %s [%d] %d\n",
           entry->d_ino, entry->d_name, entry->d_type, entry->d_reclen);
   };
 
   closedir(dir);
 
ну findfirst/findnext в крайнем случае, а это вы хрень какую-то написали. Улыбающийся

И да, так я писал на C (года так до 93), как только у меня появился C++, я сразу сделал для него специальный класс и долгое время использовал его. А потом в boost сделали filesystem.
« Последнее редактирование: Июль 22, 2014, 15:18 от Old » Записан
Страниц: [1] 2 3 4   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.101 секунд. Запросов: 23.