Дайте примеры циклов с иттераторами

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Дайте примеры циклов с иттераторами и самописных контейнеров с иттераторами.

Код: Выделить всё

struct TList
{
 int Data;
 TList *Next;
};
и требуется вывести в консоль список, начиная с указателя Start и до конца. На указателях это будет

Код: Выделить всё

struct TList
{
 int Data;
 TList *Next;
};
TList *Start;
...
TList *p;
for (p=Start; p!=nullptr; p=p->Next)
{
 std::cout<<p->Data<<std::endl;
}
. А как это будет на иттераторах? Как создавать типы иттераторов для своих контейнеров? Как правильно писать условие завершения цикла?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
WinMain
Сообщения: 929
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

Примерно так используется шаблон списка из стандартной библиотеки STL...

Код: Выделить всё

#include <list>

typedef struct _MYDATA_
{
	// Some fields...
} MYDATA;

int main(int argc, char* argv[])
{
	MYDATA md1 = {/*init fields*/};
	MYDATA md2 = {/*init fields*/};
	MYDATA md3 = {/*init fields*/};
	std::list<MYDATA> mdl;
	std::list<MYDATA>::iterator it;
	mdl.push_back(md1);
	mdl.push_back(md2);
	mdl.push_back(md3);
	for (it = mdl.begin(); it != mdl.end(); ++it)
	{
		MYDATA& md = *it;
		// TODO: use md
	}

	return 0;
}
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

А так:

Код: Выделить всё

#include <iostream>

class TList
{
 public:
 class TItem;
 class TItterator
 {
  private:
   friend class TList;
   friend class TItem;
   TItem *Pointer;
   TItterator (TItem *Pointer)
   {
    this->Pointer=Pointer;
   }
  public:
   TItterator ()
   {
   }
   TItterator operator ++()
   {
    if (Pointer!=nullptr)
    {
     Pointer=Pointer->Next;
    }
   }
   TItem & operator * ()
   {
    return *Pointer;
   }
   bool operator != (const TItterator &Right)
   {
    return (Pointer!=Right.Pointer);
   }
 };
 class TItem
 {
  friend class TList;
  friend class TItterator;
  private:
   TItem *Next;
  public:
   int Data;
 };
 private:
  TItem *Start;
  TItem *Last;
 public:
  TList()
  {
   Start=nullptr;
   Last =nullptr;
  }
  void Add(int Data)
  {
   TItem *Item;
   Item=new TItem;
   Item->Data=Data;
   Item->Next=nullptr;
   if (Last!=nullptr)
   {
    Last->Next=Item;
   }
   Last=Item;
   if (Start==nullptr)
   {
    Start=Item;
   }
  }
  TItterator Begin()
  {
   return TItterator(Start);
  }
  TItterator End ()
  {
   return TItterator(nullptr);
  }
};

int main()
{
 TList List;
 TList::TItterator Itterator;
 List.Add(2);
 List.Add(21);
 List.Add(3);
 List.Add(4);
 List.Add(8);
 List.Add(5);
 List.Add(1);
 for (Itterator=List.Begin(); Itterator!=List.End(); ++Itterator)
 {
  std::cout<<(*Itterator).Data<<std::endl;
 }
 return 0;
}
правильно? End должен указывать за конец? Куда должен указывать иттератор, созданный конструктором по умолчанию? В него надо завернуть nullptr? Или как? А оператор -> в классе иттератора должен быть? Как его правильно написать?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
WinMain
Сообщения: 929
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

Вообще по-хорошему метод end() не должен возвращать нулевой указатель.
Лучше вместо nullptr объявить внутри класса некий пустой элемент итератора, который всегда будет являться завершающим.
По аналогии с текстовой строкой, которая завершается нулевым символом. Даже если ты объявишь в коде пустую строку, в ней всё равно будет присутствовать один завершающий символ.
Так же и в списке. Пока в нём ещё нет элементов, методы begin() и end() будут возвращать указатель на этот пустой элемент итератора.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

То есть пустоту проверять по полю Data. Ну просто прекрасно. А если это не char и не действительное число? Или действительное, но nan должен поддерживаться, как обычный, а не терминальный элемент? Специальное флаговое поле заводить? И вообще это внутренности, а вопрос был об интерфейсе, за исключением оператора ->. End указывает за конец? Или на конец? Оператор -> надо поддерживать? Какой у него синтаксис перегрузки? Как его реализовать?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
WinMain
Сообщения: 929
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

Пустоту завершающего элемента проверять не нужно. Тебе нужен лишь указатель на этот элемент.
А метод end() всегда должен возвращать именно этот указатель.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

А так:

Код: Выделить всё

#include <iostream>

class TList
{
 public:
 class TItem;
 class TItterator
 {
  private:
   friend class TList;
   friend class TItem;
   TItem *Pointer;
   TItterator (TItem *Pointer)
   {
    this->Pointer=Pointer;
   }
  public:
   TItterator ()
   {
    Pointer=nullptr;
   }
   TItterator operator ++()
   {
    if (Pointer!=nullptr)
    {
     Pointer=Pointer->Next;
    }
   }
   TItem &operator * ()
   {
    return *Pointer;
   }
   TItem *operator -> ()
   {
    return Pointer;
   }
   bool operator != (const TItterator &Right)
   {
    return (Pointer!=Right.Pointer);
   }
 };
 class TItem
 {
  friend class TList;
  friend class TItterator;
  private:
   TItem *Next;
  public:
   int Data;
 };
 private:
  TItem *Start;
  TItem *Last;
 public:
  TList()
  {
   Start=nullptr;
   Last =nullptr;
  }
  TItterator Insert(const TItterator &Itterator, const int &Data)
  {
   TItem *Item;
   Item=new TItem;
   Item->Data=Data;
   if (Itterator.Pointer==nullptr)
   {
    if (Start==nullptr)
    {
     Start=Item;
     Last =Item;
    }
    else
    {
     Last->Next=Item;
     Last=Item;
    }
    Item->Next=nullptr;
   }
   else
   {
    Item->Next=Itterator.Pointer->Next;
    Itterator.Pointer->Next=Item;
   }
   return TItterator(Item->Next);
  }
  TItterator Begin()
  {
   return TItterator(Start);
  }
  TItterator End ()
  {
   return TItterator(nullptr);
  }
};

int main()
{
 TList List;
 TList::TItterator Itterator;
 Itterator=List.Begin();
 Itterator=List.Insert(Itterator, 2);
 Itterator=List.Insert(Itterator, 21);
 Itterator=List.Insert(Itterator, 3);
 Itterator=List.Insert(Itterator, 4);
 Itterator=List.Insert(Itterator, 8);
 Itterator=List.Insert(Itterator, 5);
 Itterator=List.Insert(Itterator, 1);
 for (Itterator=List.Begin(); Itterator!=List.End(); ++Itterator)
 {
  std::cout<<Itterator->Data<<std::endl;
 }
 return 0;
}
? Не касаясь внутренностей End().
WinMain писал(а):Пустоту завершающего элемента проверять не нужно. Тебе нужен лишь указатель на этот элемент.
А метод end() всегда должен возвращать именно этот указатель.
Не понял. Но реализацию End прошу пока отложить. Скажите, куда он должен указывать. На конец, или за конец? А потом уже реализацию End, как для настоящего (Изображение) американца.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
WinMain
Сообщения: 929
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

Вот как я это сделал.
Для простоты понимания в моём примере нет полей для хранимых пользовательских данных списка, но сути это не меняет...

Код: Выделить всё


class TSimpleList
{
public:
	class Node
	{
	public:
		Node(): Prev(nullptr), Next(nullptr)
		{
		}
		Node *Prev;
		Node *Next;
	};

	class Iterator
	{
	public:
		Iterator() : m_node(nullptr)
		{
		}
		Iterator(Node *n)
		{
			m_node = n;
		}
		void operator++()
		{
			if (m_node != nullptr)
			{
				m_node = m_node->Next;
			}
		}
		bool operator!=(const Iterator& it)
		{
			return (m_node != it.m_node);
		}
	private:
		Node *m_node;
	};

	TSimpleList() : m_pBegin(&m_end)
	{
	}
	~TSimpleList()
	{
		// удаление элементов списка
		while (m_end.Prev != nullptr)
		{
			Node *Last = m_end.Prev;
			m_end.Prev = Last->Prev;
			delete Last;
		}
	}
	Iterator Begin()
	{
		return m_pBegin;
	}
	Iterator End()
	{
		return &m_end;
	}
	void Add()
	{
		if (&m_end != m_pBegin)
		{
			Node *it = new Node();
			Node *Last = m_end.Prev;
			Last->Next = it;
			m_end.Prev = it;
			it->Next = &m_end;
		} else
		{
			m_pBegin = new Node();
			m_pBegin->Next = &m_end;
			m_end.Prev = m_pBegin;
		}
	}
private:
	Node m_end;
	Node* m_pBegin;
};

int _tmain(int argc, _TCHAR* argv[])
{
	TSimpleList sl;
	TSimpleList::Iterator it;
	sl.Add();
	sl.Add();
	sl.Add();

	for (it = sl.Begin(); it != sl.End(); ++it)
	{
		_puttch('.');
	}

	_gettch();

	return 0;
}

Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Издеваетесь? Куда должен указывать метод End()? На конец, или за конец? Объясните, как гурону с каменным тамогавком, который ружья то не видел и парсить Ваш код будет долго.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Эндовый итератор всегда должен указывать "за конец" (в твоей терминологии).

А что сложного, что том, чтобы "распарсить" код WinMain'а, я не понимаю. Код прозрачен и читается сразу. А вот твой код тяжело читать.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Ответить