Сортивка массива, используя итераторы

Рейтинг: 0Ответов: 1Опубликовано: 19.05.2011

Здравствуйте. Относительно недавно мне помогли разобраться с итераторами, точнее понять их реализацию и т.п. (за что спасибо тем, кто помогал). Вот создам ещё один вопрос (ибо тот именно свою задачу исчерпал).

Дело вот в чем... Прописал я эти итераторы, вроде бы понял как что к чему более менее.. Получился следующий класс:

class Array
    {
    private:
        int n;
        int* arr;

    public:
        class Iterator
        {
        public:
            int *p;
            Iterator()
            {
                p = nullptr;
            }
            Iterator(int *pv)
            {
                p = pv;
            }
            int operator*() const
            {
                return *p;
            }
            /*Iterator operator++(int)
            {
                int* temp = p;
                return ++temp;
            }*/
            Iterator& operator++()
            {
                ++p;
                return *this;
            }
            Iterator operator++(int)
            {
                return Iterator(p++);
            }
            Iterator& operator--()
            {
                --p;
                return *this;
            }
            Iterator operator--(int)
            {
                return Iterator(p--);
            }
            const int& operator[](const std::size_t&pv)
            {
                return p[pv];
            }
            Iterator& operator+=(const std::size_t&pv)
            {
                p += pv; return *this;
            }
            int* base() const
            {
                return p;
            }
            Iterator operator=(const int a)
            {
                *p = a; return *this;
            }
        };
        Iterator begin()
        {
            return Iterator(arr);
        }
        Iterator end()
        {
            return arr+n;
        }
        friend inline bool operator==(const Iterator&it1, const Iterator&it2)
        {
            return it1.base() == it2.base();
        }
        friend inline bool operator!=(const Iterator&it1, const Iterator&it2)
        {
            return it1.base() != it2.base();
        }
        friend inline bool operator <(const Iterator&it1, const Iterator&it2)
        {
            return it1.base() < it2.base();
        }
        friend inline bool operator >(const Iterator&it1, const Iterator&it2)
        {
            return it1.base() > it2.base();
        }
        friend inline bool operator <=(const Iterator&it1, const Iterator&it2)
        {
            return it1.base() <= it2.base();
        }
        friend inline bool operator >=(const Iterator&it1, const Iterator&it2)
        {
            return it1.base() >= it2.base();
        }
        friend Iterator operator+(const Iterator&it, std::size_t n)
        {
            return Array::Iterator(it.base() + n);
        }
        friend Iterator operator-(const Iterator&it, std::size_t n)
        {
            return Array::Iterator(it.base() - n);
        }
...
}

Всё вроде отлично... Но теперь я попытался переделать алгоритмы сортировки под эти итераторы. Ну.. Возьмем к примеру самый простой InsertionSort: Без итераторов было так:

void Array::InsertionSort() 
{
       int i, j, key;       
       for (i = 1; i < length; i++)     
       {
             key = arr[i];
             j = i - 1;
             while (j >= 0 && arr[j] > key)
             {
                   arr[j + 1] = arr[j]; 
                   j--; 
             }
             arr[j + 1] = key;  
       }
 }

После того, как я приделал итераторы получилось так:

void Array::InsertionSort() 
{
       int key; 
       Iterator start = Array::begin();
       Iterator end = Array::end();
       Iterator temp;
       for (++start; start != end; start++)     
       {
             key = *start;              
             temp = start - 1;
             while (temp >= Array::begin() && *temp > key)
             {
                 (temp+1) = *temp;
                 --temp;
             }
             compares++;
             (temp+1) = key;
       }
 }

Концепцию правильно понял, или не очень? Можно ли как-то это оптимизировать?

И ещё... Я хотел для сравнения элемента и сменой их мест, чтобы было что-то типо такого

*it1 = *it2;

Такая запись не получалась, поэтому я через час попыток остановился на такой перегрузке:

Iterator operator=(const int a)
            {
                *p = a; return *this;
            }

Быть может есть более оптимальный вариант? И можно ли перегрузить так, как хотел я? (*it1 = *it2);

И как быть алгоритмам сортировки таким, как шейкерная или быстрая... Та же вообще границы ... И нужно брать по индексу.. Как итераторы в таком случае использовать? - Может быть с помощью перегруженных [] или есть оптимальнее?

Ответы

▲ 2Принят

Чтобы понять, что и как "правильно" с итераторами, проще всего проводить аналогию с указателями. Указатель можно разыменовать? Можно.

int* p;
*p = 4; // присваивание ОК
int x = *p; // чтение ОК

const int* p;
*p = 4; // присваивание НЕЛЬЗЯ
int x = *p; // чтение ОК

То есть, объект итератора по поведению должен быть вообще неотличим от обычного указателя.

И значит вам надо два оператора доступа к значению:

// этот оператор будет разрешать только чтение для константных итераторов
// и, в случае, int как вы и сделали его можно заменить на
// int operator*() const
// без потери качества
const int& operator*() const
{
    return *p;
}
// этот оператор будет разрешать и чтение и запись для неконстантных итераторов
int& operator*()
{
    return *p;
}

И тогда конструкция вида

Iterator it1;
Iterator it2;
*it1 = *it2;

будет работать, потому что итераторы не константные.

Вот так тоже будет работать:

Iterator it1;
const Iterator it2;
*it1 = *it2;

А вот так уже не будет:

const Iterator it1;
const Iterator it2;
*it1 = *it2;

И если вы замените в данном случае итераторы на обычные указатели, ничего не поменяется.

Что касается, как использовать итераторы в реализации алгоритмов: представьте, что вместо итераторов у вас указатели и напишите алгоритм с их помощью. Потом поменяйте все на итераторы и, если итераторы сконструированы правильно, все должно продолжать работать.