Итератор
Объясните руским языком, что такое итератор и с чем его едят!!!
Допустим есть у тебя список
std::list<int>::iterator i;
//заполняем список
mylist.push_back(1);
mylist.push_back(2);
mylist.push_back(3);
// теперь бы нада в цикле обработать этот списочек.
for(i = mylist.begin(); i != mylist.end(); i++)
do_something(*i); // i - итератор
// *i - элемент списка типа int
Для списка пример не очень актуален, можно ведь написать и mylist.next() (или чтото в этом роде), но вот для тех же бинарных деревьев/графов такой подход не покатит, - нужен итератор.
Для списка пример не очень актуален, можно ведь написать и mylist.next() (или чтото в этом роде)
Это не совсем так, т.к. итератор используется в стандартных алгоритмах (#include <algorithm>), поэтому для списка, как одного из контейнеров, это весьма актуально.
И ещё, для итератора уже имеет смысл писать ++i, а не i++.
И ещё, для итератора уже имеет смысл писать ++i, а не i++.
Вот тут можно б и по-подробнее. В с++ есть разделение на перегрузку ++i и i++. Но какая разница итератору???? ++і и i++ ведь выполняют одинаковые действия.
или на конец коллекции.
Итератор обладает некоторыми свойствами указателя
*it возвращает объект
Но
++it переставляет указатель на следующий объект коллекции(если он имеется)
--it переставляет указатель на предыдущий объект коллекции(если он имеется)
.begin() - указатель на первый объект
.end() - указатель на КОНЕЦ коллекции,
а не последний объект!!!
В дополнение к примеру Alexandoros'а
std::map<int> MyMap;
std::map<int>::const_iterator it;
// Где-то заполнен map
// Поиск в map
if ( (it= MyMap.find(20)) != MyMap.end() )
{
// нашел; it указывает на то что содержит 20
}
Итератор обладает некоторыми свойствами указателя
*it возвращает объект
Если быть точным, то не объект, а ссылку на существующий объект, а в особом случае (bool) ссылку на прокси-объект.
Вот тут можно б и по-подробнее. В с++ есть разделение на перегрузку ++i и i++. Но какая разница итератору???? ++і и i++ ведь выполняют одинаковые действия.
Попробуй сам ответить на вопрос:
если они выполняют "одинаковые действия", то зачем тогда в С++ два оператора: префиксный (++i) и постфиксный (i++) ?
Ответ прост.
Если быть точным, то не объект, а ссылку на существующий объект, а в особом случае (bool) ссылку на прокси-объект.
На программном уровне внутреннее устройство STL слава аллаху не видна!
и если уж написал
MyType MyVar = *it;
то всегда получишь содержимое того чего надо, а не неких промежуточных инстанций, зависящих от конкретной реализации STL( Если итератор инициализирован )
Попробуй сам ответить на вопрос:
если они выполняют "одинаковые действия", то зачем тогда в С++ два оператора: префиксный (++i) и постфиксный (i++) ?
Ответ прост.
Ответ не так уж прост
В постфиксном операторе создается временный объект со временем жизни всего следующего оператора.
Создание и разрушение съедают ресурсы и именно поэтому не рекомендуется использовать постфиксные операторы
На программном уровне внутреннее устройство STL слава аллаху не видна!
и если уж написал
MyType MyVar = *it;
то всегда получишь содержимое того чего надо, а не неких промежуточных инстанций,
А если написал
MyType& MyVar = *it;
:)
зависящих от конкретной реализации STL
Ситуация с bool - это не "конкретная реализация", а стандартная вещь.
Ответ не так уж прост
Ну уж не сложен, если уместился в двух строчках... :)
Ситуация с bool - это не "конкретная реализация", а стандартная вещь.
Цитатку из стандарта можно? Для убедительности.
Ну уж не сложен, если уместился в двух строчках...
Если знаешь ответ, или делаешь вид что знаешь
Цитатку из стандарта можно? Для убедительности.
Да пожалуйста:
http://ftp.csci.csusb.edu/dick/c++std/cd2/lib-containers.html#lib.vector.bool
23.2.5 Class vector<bool>
1 To optimize space allocation, a specialization of vector for bool elements is provided:
<skip>
2 reference is a class that simulates the behavior of references of a single bit in vector<bool>.
Да пожалуйста:
http://ftp.csci.csusb.edu/dick/c++std/cd2/lib-containers.html#lib.vector.bool
Классно, но не по делу
в особом случае (bool) ссылку на прокси-объект.
Хотелось цитату, подтверждающую что оператор *итератор возвращает "в особом случае (bool) ссылку на прокси-объект", что и является сутью нашего обсуждения.
Классно, но не по делу
Хотелось цитату, подтверждающую что оператор *итератор возвращает "в особом случае (bool) ссылку на прокси-объект", что и является сутью нашего обсуждения.
Я привел достаточно ссылок, для подтверждения. Осталось только самому логически поразмыслить.
Чтож, придется разжевывать...
Для оптимизации по памяти существует частная специализация vector
23.2.5 Class vector<bool>
To optimize space allocation, a specialization of vector for bool elements is provided:
расположение элементов в vector<bool> хотя и последовательное, но отличается от стандартного
23.2.4.1
....
The elements of a vector are stored contiguously, meaning that if v is a vector<T, Allocator> where T is some type other than bool, then it obeys the identity &v[n] == &v[0] + n for all 0 <= n < v.size().
Т.о. тип bool представлен в контейнере одним битом, что исключает возможность прямой непосредственной адресации к отдельному элементу, как к типу bool. Поэтому введен прокси-класс reference
23.2.5.2
reference is a class that simulates the behavior of references of a single bit in vector<bool>.
Итератор вектора является как минимум forward итератором (23.1.1.5, 23.1.9), поэтому должен не просто возвращать значение элемента при разыменовании, а давать доступ к элементу контейнера (24.1.3). Но т.к. вернуть ссылку на объект типа bool из vector<bool> невозможно, возвращается прокси-обект класса reference.
Надеюсь, теперь моё мнение понятно?
Хотелось бы услышать твоё объяснение своей позиции.
Реализация всех особых случаев скрыта от программы (здесь можно было бы прочесть лекцию об инкапсуляции). Детали этой реализации в большинстве случаев не неинтересны
А для ответа на вопрос Shida IMHO просто вредны, тк затуманивают общую картину
Я уверждаю:
В конструкции
MyType MyVar = *Iterator
всегда получаешь значение того что в контейнер положил, и ничего другого и никакие разыменования тут не нужны.
Как и смайлики.
А теперь Green по поводу нашей дискуссии
Я не просил комментарии, я просил цитату из стандарта, на кот ты ссылаешься,
подтверждающую твое высказывание, что
*Iterator возвращает ссылку на то, что ты наз. прокси
Так что это опять не ответ на мой вопрос.
И последнее:
Замечания по поводу разжевывания и пр. оскорбительны. Могу тебя заверить: по части хамства у меня зачет с отличием, но на форуме никогда не буду опускаться до упражнений в оном.
Я уверждаю:
В конструкции
MyType MyVar = *Iterator
[COLOR=red]всегда получаешь значение[/COLOR] того что в контейнер положил, и ничего другого и [COLOR=red]никакие разыменования тут не нужны[/COLOR].
Как и смайлики.
Во-первых, *Iterator, т.е. operator* - это и есть унарный оператор разыменования. Так что он в твоем примере все же нужен.
Во-вторых, получишь или нет зависит от того, что скрывается за MyType. При
typedef bool& MyType
получишь не значение, а ошибку компиляции.
А теперь Green по поводу нашей дискуссии
Я не просил комментарии, я просил цитату из стандарта, на кот ты ссылаешься,
подтверждающую твое высказывание, что
*Iterator возвращает ссылку на то, что ты наз. прокси
Так что это опять не ответ на мой вопрос.
Если ты придираешься к тому, что я изначально написал "возвращает ссылку на прокси-объект", а не "возвращает прокси-объект", то да, я оговорился.
Но здесь было много неточностей, например, что std::map имеет два параметра шаблона, а не один...
Если же это не вышеобозначенная придирка, а мнение, что возвращение прокси-объекта при разыменовании для vector<bool> - это случай конкретной реализации, то попробуй доказать обратное цитатой из стандарта.
А то похоже на глупое упрямство с твоей стороны.
Я кажется логично объяснил свою позицию на конкретных взаимосвязанных пунктах стандарта, которые четко (хотя и не явно) определяют, положение вещей.
А от тебя пока никаких доказательств не услышал.
И последнее:
Замечания по поводу разжевывания и пр. оскорбительны. Могу тебя заверить: по части хамства у меня зачет с отличием, но на форуме никогда не буду опускаться до упражнений в оном.
Ну уж прости за резкость.
А мне вот не нравится, когда опонент в категоричной форме требует доказательств сам никак не аргументируя свою позицию.
Во-первых, *Iterator, т.е. operator* - это и есть унарный оператор разыменования. Так что он в твоем примере все же нужен.
operator* разыменовыет итератор, кто бы спорил.
Вопрос во что?
Если *It возвращает прокси, то требуется еще и преобразование к нужному типу: что-то вроде (bool)(*It). А это не нужно. Это к вопросу об инкапсуляции.
Во-вторых, получишь или нет зависит от того, что скрывается за MyType. При
typedef bool& MyType
получишь не значение, а ошибку компиляции.
Зачем же так плохо думать об оппоненте?
Не буду же я ваять list<bool&>
Если ты придираешься к тому, что я изначально написал "возвращает ссылку на прокси-объект", а не "возвращает прокси-объект", то да, я оговорился.
Но здесь было много неточностей, например, что std::map имеет два параметра шаблона, а не один...
Каюсь, грешен, имел в виду set а вышло map
Если же это не вышеобозначенная придирка, а мнение, что возвращение прокси-объекта при разыменовании для vector<bool> - это случай конкретной реализации, то попробуй доказать обратное цитатой из стандарта.
Я не ссылался на стандарт.
А то похоже на глупое упрямство с твоей стороны.
Все равно не буду упражняться
Я кажется логично объяснил свою позицию на конкретных взаимосвязанных пунктах стандарта, которые четко (хотя и не явно) определяют, положение вещей.
Обяснений было много, но ни в нигде не был упомянут оператор*, как он живет в list<bool>
А от тебя пока никаких доказательств не услышал.
А что я должен доказать?
Ну уж прости за резкость.
А мне вот не нравится, когда опонент в категоричной форме требует доказательств сам никак не аргументируя свою позицию.
Ты сослался на стандарт, я посмотрел его, но не нашел подтверждения твоим словам. Во я и попросил тебя и совсем ненавязчиво.
Я предлагаю следующее:
Ты пишешь 3-5-7 строк программного кода,
(ничего не переопределяя в STL ) и показываешь
как *Iterator возвращает прокси.
И чтоб откомпилировалось!
Это будет самым лучшим доказательством твоей правоты.
А на дискуссию на уровне "А ты кто такой" время тратить не желаю.
operator* разыменовыет итератор, кто бы спорил.
Вопрос во что?
Согласно стандарту (24.1.1.2 Table 72) в "convertible to T", а для контейнеров это T& (24.1.3 Table 74).
Если *It возвращает прокси, то требуется еще и преобразование к нужному типу: что-то вроде (bool)(*It). А это не нужно. Это к вопросу об инкапсуляции.
Инкапсуляция тут не при чем. Здесь имеет место неявное преобразование типов.
Обяснений было много, но ни в нигде не был упомянут оператор*, как он живет в list<bool>
Я говорил про vector<bool> и vector<bool>::iterator. Ты говорил, что итератор возвращает объект, я поправил, что итератор возвращает ссылку на объект и в исключительном случае возвращает прокси-объект.
А что я должен доказать?
То на чем ты настаиваешь. Видимо то, что разыменование итератора возвращает объект хранимый в контейнере и это происходит во всех случаях, даже для vector<bool>.
Или я тебя неправильно понял?
В любом случае, если ты считаешь, что я неправ, то у тебя есть правильный вариант. Вот его опиши и обоснуй.
Что бы не было дальнейших непониманий, под "возвращает объект" я понимаю
T func();
под "возвращает ссылку на объект" я понимаю
T& func();
Я предлагаю следующее:
Ты пишешь 3-5-7 строк программного кода,
(ничего не переопределяя в STL ) и показываешь
как *Iterator возвращает прокси.
И чтоб откомпилировалось!
Это будет самым лучшим доказательством твоей правоты.
Вариант 1
vector<bool>::iterator it = container.begin();
// доказательство номер 1
// bool& b = *it; // эта строчка не скомпилируется
// доказательство номер 2
// reference - это прокси-объект
vector<bool>::reference ref = *it;
ref.flip(); // у этого прокси есть метод flip(), а у bool он есть?
Вариант 2
void func(Type arg) {
class A {
Type t[];
};
}
vector<bool>::iterator it;
func(*it);
Этот код скомпилируется (такое условие ты выдвинул), но с предупреждением. Посмотри на тип упоминаемый в предупреждении и ты увидишь, что в func передается совсем не bool. Это прокси, который в различных реализациях может называться по-разному, но всегда носит второе имя reference.
Итератор это объект для обхода в цикле сложных структур даных таких как списки, очередя, деревья, графы.
:roll: Доброе время суток:)А можно ли использовать итераторы с динамическими массивами? У меня есть 3-х мерный динамический массив и мне нужно работу с ним представить, как работу с итератором.
1. Я утверждал уже 2 раза и уверждаю в 3 раз:
В конструкции
MyType MyVar = *Iterator
всегда получаешь значение того что в контейнер положил, и ничего другого и никакие разыменования тут не нужны.
Вопрос с Type& мы не рассматриваем, также как и массивы ссылок.
2. И я утверждал, что возвращаемое значение в *iterator зависит от реализации STL, т.к. я с этим сталкивался.
Ничего другого я не утверждал, я задавал вопросы и не был удовлетворен некоторыми ответами.
3. По поводу доказательств и примеров
3.1 Мелкие очепятки не рассматриваем
3.2 Якобы "доказательство номер 1"
// bool& b = *it;
Мы уже договаривались, не использовать аргументы, оскорбляющие умственные способности оппонента.
3.3 По существу:
VS 2003 компилирует все твои примеры.
VS6 SP6 образца 2004 года твое док-во номер 1 не компилирует.
Каждая конкретная РЕАЛИЗАЦИЯ STL работает несколько иначе, несмотря на наличие стандарта.
Во и все доказательство моего 2-го утверждения. С помощью твоих примеров.
Можно много говорить: Microsoft, устарело и проч. и проч.
Но это все IMHO теоретические рассуждения, а жизнь их корректирует. Исходные тексты не инвариантны к средам разработки.
PS
Ну и совсем мелочь:
В конструкции
MyType MyVar = *Iterator последней инстанцией преобразования типов является опрератор=, а преобразования инкапсулированы ....
[QUOTE]Originally posted by Alexandoros
А можно ли использовать итераторы с динамическими массивами? У меня есть 3-х мерный динамический массив и мне нужно работу с ним представить, как работу с итератором.
Если обычный массив, нечто вроде type MyArray[][][], то нельзя.
Итератор-это объект STL
Если для организации массива ты используешь vector, то можно.
Green
1. Я утверждал уже 2 раза и уверждаю в 3 раз:
В конструкции
MyType MyVar = *Iterator
всегда получаешь значение того что в контейнер положил, и ничего другого и никакие разыменования тут не нужны.
Вопрос с Type& мы не рассматриваем, также как и массивы ссылок.
Ок. Согласен.
Green
2. И я утверждал, что возвращаемое значение в *iterator зависит от реализации STL, т.к. я с этим сталкивался.
Подожди... Ты хотел привязку к стандарту, я привязал.
Forward итератор, должен давать доступ к элементу контейнера, в общем случае это происходит возвращением ссылки на элемент. Об этом четко говорит стандарт.
С этим ты согласен?
К элементу vector<bool> достум может быть осуществлет только через прокси из-за вышеописанных особенностей этого контейнера. С этим ты согласен?
Если ты с этим не согласен, приведи, плз, пример доказывающий, что доступ к элементу vector<bool> возможен без прокси. Все равно, что будет доказательством, пример из конкретной реализации STL (при этом эта реализация должна отвечать пункту 23.2.5.2), либо собственоручно написанный итератор (опять же для контейнера соотв. пункту 23.2.5.2).
3.2 Якобы "доказательство номер 1"
// bool& b = *it;
Мы уже договаривались, не использовать аргументы, оскорбляющие умственные способности оппонента.
Никто никого не оскорблял. Для типа int (как и для других) я могу сделать такую запись
vector<int>::iterator it;
int& i = *it;
а для bool уже не могу. Что здесь оскорбительного?
VS 2003 компилирует все твои примеры.
VS6 SP6 образца 2004 года твое док-во номер 1 не компилирует.
VS6 вообще очень слабо соответствует стандарту, а в вопросе шаблонов, можно сказать, совсем не соответствует.
Вот ещё одно доказательство того, что разыменование итератора для vector<bool> возвращает прокси, а не bool:
#include <vector>
template<typename T>
void func(T)
{
std::cout << "unspecified" << std::endl;
};
template<>
void func(bool)
{
std::cout << "bool" << std::endl;
};
template<>
void func(std::vector<bool>::reference)
{
std::cout << "std::vector<bool>::reference" << std::endl;
};
int main()
{
std::vector<bool>::iterator it;
func(*it);
return 0;
}
Предупреждаю сразу, что на VS6 его можно даже не пытаться компилировать, т.к. VS6 не поддерживает частную специализацию.
Каждая конкретная РЕАЛИЗАЦИЯ STL работает несколько иначе, несмотря на наличие стандарта.
Во и все доказательство моего 2-го утверждения. С помощью твоих примеров.
Тогда зачем просить приводить цитаты из стандарта, для убедительности?
то все IMHO теоретические рассуждения, а жизнь их корректирует. Исходные тексты не инвариантны к средам разработки.
Пеняя на неидеальность реализации, можно объяснить вообще все.
В конструкции
MyType MyVar = *Iterator последней инстанцией преобразования типов является опрератор=, а преобразования инкапсулированы ....
Приведи, пожалуйста цитату из стандарта "для убедительности", что тип bool имеет оператор
operator=(const vector<bool>::reference&)
Или ты не это имел в виду?
Честно говоря, мне не понятна фраза "преобразования инкапсулированы". Что она значит? (Что такое инкапсуляция я знаю).
А можно ли использовать итераторы с динамическими массивами? У меня есть 3-х мерный динамический массив и мне нужно работу с ним представить, как работу с итератором.
Итератор-это объект STL
Если для организации массива ты используешь vector, то можно.
Я (уже) прошу прощения, но итератор - это в общем понимании паттерн проектирования или др. словами один из общепринятых способов (методов), а уж как частный случай - это реализация в STL на языке С++.
Поэтому, задание представить работу с динамическим массивом через итераторы можно расценить по-разному.
Никто не мешает создать свой итератор для обхода своего динамического массива, и для этого тебе не нужно превязываться к STL (хотя и можно).
Обобщенное описание итератора приведено здесь:
http://ooad.asf.ru/patterns/patterninfo.asp?id=19
В простейшем случае для массива итератором будет просто указатель на элемент массива.
Т.е. для твоего трехмерного массива, принимая во внимание, что в С++ все элементы будут расположены в памяти линейно, простейшим итератором будет просто указатель на элемент:
int* it = &MyArray[0][0][0]; // инициализируем итератор адресом первого элемента
Естественно, такой итератор не отслеживает выход за пределы массива. Для этого можно написать соотв. класс итератора, с интерфейсом предоставляющим как минимум следующую функциональность:
1) шаг вперед (operator++), ;
2) (опционально) шаг назад (operator--), с проверкой на границу массива;
3) доступ к текущему элементу (operator*, operator->).