Страница 1 из 2

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

Добавлено: 14 янв 2016, 11:05
Сионист
Дайте примеры циклов с иттераторами и самописных контейнеров с иттераторами.

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

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;
}
. А как это будет на иттераторах? Как создавать типы иттераторов для своих контейнеров? Как правильно писать условие завершения цикла?

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

Добавлено: 14 янв 2016, 11:29
WinMain
Примерно так используется шаблон списка из стандартной библиотеки 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;
}

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

Добавлено: 14 янв 2016, 11:58
Сионист
А так:

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

#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? Или как? А оператор -> в классе иттератора должен быть? Как его правильно написать?

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

Добавлено: 14 янв 2016, 13:33
WinMain
Вообще по-хорошему метод end() не должен возвращать нулевой указатель.
Лучше вместо nullptr объявить внутри класса некий пустой элемент итератора, который всегда будет являться завершающим.
По аналогии с текстовой строкой, которая завершается нулевым символом. Даже если ты объявишь в коде пустую строку, в ней всё равно будет присутствовать один завершающий символ.
Так же и в списке. Пока в нём ещё нет элементов, методы begin() и end() будут возвращать указатель на этот пустой элемент итератора.

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

Добавлено: 14 янв 2016, 13:36
Сионист
То есть пустоту проверять по полю Data. Ну просто прекрасно. А если это не char и не действительное число? Или действительное, но nan должен поддерживаться, как обычный, а не терминальный элемент? Специальное флаговое поле заводить? И вообще это внутренности, а вопрос был об интерфейсе, за исключением оператора ->. End указывает за конец? Или на конец? Оператор -> надо поддерживать? Какой у него синтаксис перегрузки? Как его реализовать?

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

Добавлено: 14 янв 2016, 14:08
WinMain
Пустоту завершающего элемента проверять не нужно. Тебе нужен лишь указатель на этот элемент.
А метод end() всегда должен возвращать именно этот указатель.

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

Добавлено: 14 янв 2016, 15:10
Сионист
А так:

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

#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, как для настоящего (Изображение) американца.

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

Добавлено: 14 янв 2016, 16:12
WinMain
Вот как я это сделал.
Для простоты понимания в моём примере нет полей для хранимых пользовательских данных списка, но сути это не меняет...

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


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;
}


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

Добавлено: 14 янв 2016, 17:04
Сионист
Издеваетесь? Куда должен указывать метод End()? На конец, или за конец? Объясните, как гурону с каменным тамогавком, который ружья то не видел и парсить Ваш код будет долго.

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

Добавлено: 14 янв 2016, 17:32
Romeo
Эндовый итератор всегда должен указывать "за конец" (в твоей терминологии).

А что сложного, что том, чтобы "распарсить" код WinMain'а, я не понимаю. Код прозрачен и читается сразу. А вот твой код тяжело читать.