Как сюда прикрутить исключение?

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

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

Как сюда:

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

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()
{
    TSimpleList sl;
    TSimpleList::Iterator it;
    sl.Add();
    sl.Add();
    sl.Add();

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

    _gettch();

    return 0;
}
прикрутить исключение разыменовании иттератора, равного sl.End()? Здесь:

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

class TList
{
 public:
 class TItem;
 class itterator
 {
  private:
   friend class TList;
   friend class TItem;
   TItem *Pointer;
   itterator (TItem *Pointer)
   {
    this->Pointer=Pointer;
   }
  public:
   itterator ()
   {
    Pointer=nullptr;
   }
   itterator operator ++()
   {
    if (Pointer!=nullptr)
    {
     Pointer=Pointer->Next;
    }
   }
   int &operator * ()
   {
    return Pointer->Data;
   }
   bool operator != (const itterator &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;
  }
  ~TList()
  {
   TItem *Next;
   for (Next=Start->Next; Next!=nullptr; Start=Next, Next=Start->Next)
   {
    delete Start;
   }
   Last=nullptr;
  }
  itterator Insert(const itterator &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 itterator(Item->Next);
  }
  itterator Begin()
  {
   return itterator(Start);
  }
  itterator End ()
  {
   return itterator(nullptr);
  }
};
ничего прикручивать не надо: разыменование nullptr уже ведёт к аварийному завершению. А в процитированном варианте?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Сионист писал(а):ничего прикручивать не надо: разыменование nullptr уже ведёт к аварийному завершению
Аварийное завершение и генерация исключения - это не одно и то же. Аварийного завершения в коде не должно быть в принципе никогда. Так что если собираешься писать код, который будет разыменовывать итератор без проверки, то в любом случае внутри оператора разыменования нужно будет делать эту проверку и кидать исключение.

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

Вот как-то так...

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

#include <exception>
#include <iostream>
#include <locale.h>

using namespace std;

template <typename T>
class TSimpleList
{
public:
	class TNode
	{
	public:
		TNode(): Prev(nullptr), Next(nullptr)
		{
		}
		TNode *Prev, *Next;
		T Data;
	};

	class Iterator
	{
	public:
		Iterator(TNode *n = nullptr, TSimpleList *pList = nullptr)
		{
			m_node = n;
			m_pList = pList;
		}
		void operator++()
		{
			if (m_node != nullptr)
			{
				m_node = m_node->Next;
			}
		}
		bool operator!=(const Iterator& it)
		{
			return (m_node != it.m_node);
		}
		T& operator *()
		{
			if (m_node == nullptr)
			{
				throw exception("Обращение к данным по нулевому указателю");
			} else
				if (m_pList != nullptr && m_node == &m_pList->m_end)
			{
				throw exception("Обращение к итератору завершения списка");
			}
			return m_node->Data;
		}

	private:
		TNode *m_node;
		TSimpleList *m_pList;
	};

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

int _tmain(int argc, _TCHAR* argv[])
{
	_tsetlocale(LC_ALL, _T("russian"));

	// Проверка исключения...
	try 
	{
		TSimpleList<long> sl;
		TSimpleList<long>::Iterator it = sl.End();
		cout << *it << ' ';
	}
	catch (exception& ex)
	{
		printf("%s\n", ex.what());
	}

	// Штатный режим работы
	TSimpleList<long> sl;
	TSimpleList<long>::Iterator it;
	sl.Add(10);
	sl.Add(20);
	sl.Add(50);
	sl.Add(100);
	sl.Add(250);

	for (it = sl.Begin(); it != sl.End(); ++it)
	{
		cout << *it << ' ';
	}

	_gettch();

	return 0;
}

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

Romeo писал(а):Аварийное завершение и генерация исключения - это не одно и то же. Аварийного завершения в коде не должно быть в принципе никогда. Так что если собираешься писать код, который будет разыменовывать итератор без проверки, то в любом случае внутри оператора разыменования нужно будет делать эту проверку и кидать исключение.
это вот так:

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

int *operator *()
{
 if (Node==m_end)
 {
  return *nullptr;
 }
 return Node->Data;
}
что ли? Коряво, лучше уж в енде nullptr возвращать.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Разыменование nullptr? Это не просто коряво... За это и уволить могут :)
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Это оболочка над указателем, отличающаяся тем, что в иттератор инкапсулирован перебор к следующему элементу с учётом его фактического размещения в памяти.

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

struct TListItem
{
  TListItem *Next;
  int Data;
};
TListItem *First;
...
TListItem *p;
for (p=First; p!=nullptr; p=p->Next
{
 ...
}
(*p).Data=...
Ну ка поведение последней строки в студию?

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

int *Data=new int [1024];
...
int *p;
for (p=Data; p<=Data+1023; ++p)
{
 ...
}
*p=...
Здесь указатель ещё хуже: мало того, что не валидный, так об этом может быть ещё и неизвестно. А если сразу за Data будет выделена память по другому указателю для элемента списка, или для другого массива? Хотя мне сложно представить, как это можно реализовать. Но стоит ли на это полагаться? Понятно, что допускать выход за границу нельзя, но это не повод так кардинально менять поведение.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Я не очень понял, это ты пример плохого кода привёл, или что? Рызыменование за пределами цикла перебора, конечно, не допустимо!
Сионист писал(а):Понятно, что допускать выход за границу нельзя, но это не повод так кардинально менять поведение.
Эту мысль не уловил.

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