프로젝트/STL 자료구조 구현

STL 자료구조 구현 (6) - 이터레이터 구조 변경

오의현 2024. 9. 9. 19:12

기존에는 이터레이터가 벡터에 중첩클래스로 종속되어 있었고 이를 이용하는 형식이었는데, STL의 구조를 면밀히 살펴보니 Iterator은 완전히 별개의 클래스고 자료구조는 여기에 데이터를 담아 반환해줄 뿐인 것 같았다.

 

그래서 이터레이터를 아예 별개의 클래스로 분리해주었다.

이터레이터의 종류는 InputIterator, OutputIterator, forwardIterator, BiDirectionalIterator, RandomAccessIterator로 총 5가지가 있다고 하는데, 난 일단 벡터에서 사용할 RandomAccessIterator만 구현하였다.

template <typename DataType>
class RandomAccessIterator
{
public:
    RandomAccessIterator()
    {
    }

    RandomAccessIterator(const DataType* _DataPtr)
    {
        DataPtr = const_cast<DataType*>(_DataPtr);
    }

    RandomAccessIterator(const RandomAccessIterator& _Other)
    {
        DataPtr = _Other.DataPtr;
    }

    RandomAccessIterator& operator=(const RandomAccessIterator& _Other)
    {
        DataPtr = _Other.DataPtr;
        return *this;
    }

    RandomAccessIterator& operator=(RandomAccessIterator&& _Other) noexcept
    {
        DataPtr = _Other.DataPtr;
        _Other.DataPtr = nullptr;

        return *this;
    }

    bool operator==(const RandomAccessIterator& _Other)
    {
        return (DataPtr == _Other.DataPtr);
    }

    bool operator!=(const RandomAccessIterator& _Other)
    {
        return !(*this == _Other);
    }
    
    RandomAccessIterator& operator++()
    {
        (DataPtr)++;
        return *this;
    }

    RandomAccessIterator operator++(int)
    {
        RandomAccessIterator ReturnIter(*this);
        ++(*this);
        return ReturnIter;
    }

    RandomAccessIterator& operator--()
    {
        (DataPtr)--;
        return *this;
    }

    RandomAccessIterator operator--(int)
    {
        RandomAccessIterator ReturnIter(*this);
        --(*this);
        return ReturnIter;
    }
     
    RandomAccessIterator& operator+(int _Offset)
    {
        DataPtr += _Offset;
        return *this;
    }

    RandomAccessIterator& operator-(int _Offset)
    {
        DataPtr -= _Offset;
        return *this;
    }

    DataType& operator*()
    {
        return *(DataPtr);
    }

    void Debug()
    {
        std::cout << *(DataPtr);
    }

private:
    DataType* DataPtr = nullptr;
};

bool을 제외한 자료형에 대해서 위와 같이 선언해주었다. 기존의 이터레이터와 정의는 완전히 동일하다.

따로 수정을 안해도 될 것 같아서 그대로 외부로 꺼내기만 했는데, 이는 추후 테스트를 좀 해봐야 할 듯 하다.

 

bool타입이 문제가 좀 많았다. 먼저, *연산자를 사용하면 bool값을 반환해야 하는 동시에 *연산자로 반환된 데이터에 =연산자를 통해 값을 바꿀 수도 있어야 했다. 아무래도 bool 타입은 비트단위로 관리되다 보니 일반적인 포인터로 구현하는 것이 불가능했는데, 이를 중첩클래스를 활용해 해결하였다. 먼저 코드 전문을 보자.

template<>
class RandomAccessIterator<bool>
{
    class BitReference;

public:
    RandomAccessIterator()
    {
    }

    RandomAccessIterator(unsigned int* _DataPtr, size_t _BitIndex)
    {
        DataPtr = _DataPtr;
        BitIndex = _BitIndex;
    }

    RandomAccessIterator(const RandomAccessIterator& _Other)
    {
        DataPtr = _Other.DataPtr;
        BitIndex = _Other.BitIndex;
    }

    RandomAccessIterator& operator=(const RandomAccessIterator& _Other)
    {
        DataPtr = _Other.DataPtr;
        BitIndex = _Other.BitIndex;

        return *this;
    }

    RandomAccessIterator& operator=(RandomAccessIterator&& _Other) noexcept
    {
        DataPtr = _Other.DataPtr;
        BitIndex = _Other.BitIndex;
        _Other.DataPtr = nullptr;

        return *this;
    }

    bool operator==(const RandomAccessIterator& _Other)
    {
        return (DataPtr == _Other.DataPtr && BitIndex == _Other.BitIndex);
    }

    bool operator!=(const RandomAccessIterator& _Other)
    {
        return !(*this == _Other);
    }

    RandomAccessIterator& operator++()
    {
        BitIndex++;

        if (BitIndex >= 32)
        {
            BitIndex = 0;
            DataPtr++;
        }

        return *this;
    }

    RandomAccessIterator operator++(int)
    {
        RandomAccessIterator ReturnIter(*this);
        ++(*this);
        return ReturnIter;
    }

    RandomAccessIterator& operator--()
    {
        BitIndex--;

        if (BitIndex < 0)
        {
            BitIndex = 31;
            DataPtr--;
        }

        return *this;
    }

    RandomAccessIterator operator--(int)
    {
        RandomAccessIterator ReturnIter(*this);
        --(*this);
        return ReturnIter;
    }

    RandomAccessIterator operator+(int _Offset)
    {
        size_t ReturnBitIndex = BitIndex + _Offset;
        unsigned int* ReturnDataPtr = DataPtr;

        while (ReturnBitIndex >= 32)
        {
            ReturnBitIndex -= 32;
            ReturnDataPtr++;
        }

        return RandomAccessIterator(ReturnDataPtr, ReturnBitIndex);
    }

    RandomAccessIterator operator-(int _Offset)
    {
        size_t ReturnBitIndex = BitIndex - _Offset;
        unsigned int* ReturnDataPtr = DataPtr;

        while (ReturnBitIndex >= 32)
        {
            ReturnBitIndex += 32;
            ReturnDataPtr--;
        }

        return RandomAccessIterator(ReturnDataPtr, ReturnBitIndex);
    }

    BitReference operator*()
    {
        return BitReference(DataPtr, BitIndex);
    }

    void Debug()
    {
        std::cout << *(DataPtr);
    }

private:
    class BitReference
    {
    public:
        BitReference(const unsigned int* _DataPtr, size_t _BitIndex)
        {
            DataPtr = const_cast<unsigned int*>(_DataPtr);
            BitIndex = _BitIndex;
        }

        operator bool() const
        {
            return (*DataPtr) & (1 << BitIndex);
        }


        void operator=(bool _Value)
        {
            if (_Value == true)
            {
                (*DataPtr) |= (1 << BitIndex);
            }
            else
            {
                (*DataPtr) &= ~(1 << BitIndex);
            }
        }

    private:
        unsigned int* DataPtr = nullptr;
        size_t BitIndex = 0;
    };

private:
    unsigned int* DataPtr = nullptr;
    size_t BitIndex = 0;
};

먼저, *연산자를 호출하면 BitReference라는 클래스를 생성하여 이를 반환하도록 하였다. 그리고 BitReference 클래스 내부에 = 연산자를 오버로딩하여 이를 통해 값을 바꿀 수 있도록 하였다.

 

또한, *연산자를 통해 bool 값도 반환받을 수 있어야 하기 때문에 BitReference 클래스에 bool타입으로의 캐스팅 연산자를 오버로딩하여 값을 받을 수 있도록 하였다.