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

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

Добавлено: 18 янв 2016, 09:42
Сионист
Как сюда:

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

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 уже ведёт к аварийному завершению. А в процитированном варианте?

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

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

В самой "прикрутке" исключения нет ничего сложно. Можно просто объявить внутренний класс-исключение и в нужном месте кидать его объект.

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

Добавлено: 18 янв 2016, 15:15
WinMain
Вот как-то так...

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

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


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

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

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

int *operator *()
{
 if (Node==m_end)
 {
  return *nullptr;
 }
 return Node->Data;
}
что ли? Коряво, лучше уж в енде nullptr возвращать.

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

Добавлено: 18 янв 2016, 17:55
Romeo
Разыменование nullptr? Это не просто коряво... За это и уволить могут :)

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

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

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

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 будет выделена память по другому указателю для элемента списка, или для другого массива? Хотя мне сложно представить, как это можно реализовать. Но стоит ли на это полагаться? Понятно, что допускать выход за границу нельзя, но это не повод так кардинально менять поведение.

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

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

Ещё раз повторю. Выход из приложения с критической ошибкой в официальной версии программы - это признак того, что она написана низкоквалифицированными программистами. В коммерческом продукте abnormal termination быть не должно и точка. И никаких исключений из этого правила нет.