카테고리 없음
템플릿 클래스를 이용한 리스트 구현
solie75
2022. 6. 10. 14:20
<main.cpp>
#include <iostream>
#include <stdio.h>
#include "practice.h"
using std::cout;
using std::endl;
using std::cin;
int main()
{
// 1. 리스트 생성
CList<int> myList;
// 2. 노드 추가 ( fornt & back )
for (int i = 0; i < 5; ++i)
{
myList.Push_Back(i + 1);
}
for (int i = 0; i < 5; ++i)
{
myList.Push_Front((i + 1) * 100);
}
// 3. 리스트 출력 ( begin(), all )
cout << "리스트 첫번째 노드 " << *(myList.begin()) << endl << endl;
for (int i = 0; i < myList.size(); ++i)
{
cout << "리스트 " << i + 1 << " 번째 노드 값 = " << myList[i] << endl;
}
cout << endl;
// 4. 리스트 reverse
myList.reverse();
// 5. 리스트 출력
for (int i = 0; i < myList.size(); ++i)
{
cout << "reverse한 리스트 " << i + 1 << " 번째 노드 값 = " << myList[i] << endl;
}
cout << endl;
// 6. 리스트 삭제
// 6-1. HeadNode 삭제
myList.erase(myList.begin());
// 6-2. TailNode 삭제
//CList<int>::iterator iter = myList.begin();
//iter;;
//iter = myList.end();
//--iter;
//myList.erase(iter);
myList.erase(--myList.end());
// 6-3. 리스트 삭제
for (CList<int>::iterator iter = myList.begin(); iter != myList.end(); iter = myList.begin())
{
myList.erase(iter);
}
myList.Push_Back(111);
for (int i = 0; i < myList.size(); ++i)
{
cout << myList[i] << endl;
}
return 0;
}
<practice.h>
#pragma once
#include <assert.h>
template<typename T> // 하나의 노드에 대한 구조체
struct tListNode
{
T data;
tListNode<T>* pNext;
tListNode<T>* pPrev;
tListNode() // 인자 없이 선언만 했을 경우
: pNext(nullptr)
, pPrev(nullptr)
{
}
tListNode(const T& _data, tListNode<T>* _pNext = nullptr, tListNode<T>* _pPrev = nullptr) // 인자 있이 선언하는 경우
: data(_data)
, pNext(_pNext)
, pPrev(_pPrev)
{
}
};
template <typename T>
class CList
{
private:
tListNode<T>* pHead;
tListNode<T>* pTail;
int iCurCount;
public:
class iterator; // 클래스 CList 의 내부 함수 iterator 선언
iterator begin(); // iterator 에 대한 함수 선언
iterator end();
iterator erase(const iterator& _iter);
public:
int size() { return iCurCount; };
void Push_Back(const T& _data);
void Push_Front(const T& _data);
void reverse();
void Reverse(iterator _iter);
public:
CList()
: iCurCount(0)
, pHead(nullptr)
, pTail(nullptr)
{
}
~CList()
{
// 할당된 메로리 해제 코드
tListNode<T>* pNode = pHead;
while (nullptr != pNode)
{
tListNode<T>* pNext = pNode->pNext;
delete pNode;
pNode = pNext;
}
}
T& operator [](int _idx)
{
tListNode<T>* NextNode = this->pHead;
for (int i = 0; i < _idx; ++i)
{
NextNode = NextNode->pNext;
}
return NextNode->data;
}
class iterator // Q. 위에 선언 하고 구현은 따로 하는 이유는 무엇인가?
{
private: // iterator 는 자신이 어떤 배열(리스트)을 가리키는지 그리고 어떤 요소(노드)를 가리키는 지 이 두 가지가 필요하다. 이 두가지는 수정 되어서는 안된다.
CList<T>* pList;
tListNode<T>* pTargetNode;
public: // 오퍼레이터 모음 ==, !=, ++(전위, 후위), --(전위, 후위) , *
bool operator == (const iterator& _otheriter) // 두 개의 iterator에 대해서
{
if (pList == _otheriter.pList && pTargetNode == _otheriter.pTargetNode)
{
return true;
}
return false;
}
bool operator != (const iterator& _otheriter)
{
/*if (pList != _otheriter.pList || tListNode != _otheriter.pTargetNode)
{
return false;
}
return true;*/ // 이 코드는 위의 operator == 의 역과 같다.
return !((*this) == _otheriter);
}
iterator& operator ++ () // Q. 전위 증산연산자 // 왜 iterator 가 아니고 iterator& 인가
{
// end iterator 일 경우
assert(pTargetNode);
pTargetNode = pTargetNode->pNext;
return *this;
}
iterator operator ++ (int) // 후위 증산 연산자 // 왜 int 를 인자로 받고 iterator& 가 아니라 iterator 인가
{
iterator copy_iter(*this);
++(*this);
return copy_iter; // 실제로 iterator는 증산 했지만 증산 이전의 원래 값을 반환하여 후위 연산자 처럼 보이게 함
}
iterator operator --()
{
// 주어진 iterator 가 가리키는 노드가 pHead 일 경우
assert(pTargetNode != pList->pHead);
// 주어진 iterator 가 가리키는 노드가 pTail 일 경우
if (nullptr == pTargetNode)
{
this->pTargetNode = this->pList->pTail;
}
// 주어진 iterator 가 가리키는 노드가 중간 노드인 경우
else
{
this->pTargetNode = this->pTargetNode->pPrev;
}
return *this;
}
iterator operator --(int)
{
iterator copy_iter(*this);
--(*this);
return copy_iter;
}
T& operator * ()
{
assert(pTargetNode);
return pTargetNode->data;
}
public: // iterator 생성자와 소멸자
iterator()
: pList(nullptr)
, pTargetNode(nullptr)
{
}
iterator(CList<T>* _pList, tListNode<T>* _pTargetNode)
: pList(_pList)
, pTargetNode(_pTargetNode)
{
}
~iterator()
{
}
template<typename T>
friend class CList;
};
};
template<typename T>
void CList<T>::Push_Back(const T& _data)
{
tListNode<T>* newNode = new tListNode<T>(_data); // 새로운 노드의 메모리를 동적 할당 한다 // tListNode 의 인자 있이 선언 하는 경우로 new 을 사용하여 동적할당한다.
if (nullptr == this->pHead)
{
pHead = newNode;
pTail = newNode;
}
else
{
pTail->pNext = newNode;
newNode->pPrev = pTail;
pTail = newNode;
}
++(this->iCurCount);
}
template<typename T>
void CList<T>::Push_Front(const T& _data)
{
tListNode<T>* newNode = new tListNode<T>(_data);
if (nullptr == pHead)
{
pHead = newNode;
pTail = newNode;
}
else
{
pHead->pPrev = newNode;
newNode->pNext = pHead;
pHead = newNode;
}
++(this->iCurCount);
}
template<typename T> // inner class 가 템플릿에서 반환값으로 지정된 경우 앞에 typename키워드(대상이 inner class 타입임을 알려주는 역할)를 적어 주어야 한다.
typename CList<T>::iterator CList<T>::begin()
{
return iterator(this,pHead);
}
template<typename T>
typename CList<T>::iterator CList<T>::end()
{
return iterator(this, nullptr); // end() 함수는 마지막 노드인 pTail 의 다음 노드
}
template<typename T>
typename CList<T>::iterator CList<T>::erase(const iterator& _iter)
{
// 반환할 iterator
iterator NextIter;
// 삭제하는 노드가 첫번째 노드인 경우
if (pHead == _iter.pTargetNode)
{
// 해당 노드의 다음 노드를 알아낸다.
tListNode<T>* pNode = _iter.pTargetNode->pNext;
// 다음 노드를 첫번째(헤드) 노드로 지정한다.
pHead = pNode;
// 데이터가 2개 이상일 경우. ( pHead == pTail 일 경우 (데이터가 한 개일 경우) pHead->pPrev 는 존재 하지 않으므로 )
if (nullptr != pHead)
{
pHead->pPrev = nullptr;
}
// 다음 노드가 존재하지 않았다. (데이터가 1개 있는 상태일 경우)
else
{
pTail = nullptr;
}
// 새로운 헤드노드가 곧 삭제된 다음 노드이다.
NextIter.pTargetNode = pHead;
}
// 삭제하는 노드가 마지막 노드인 경우
else if (pTail == _iter.pTargetNode)
{
// 해당 노드의 이전 노드를 알아낸다.
tListNode<T>* pNode = _iter.pTargetNode->pPrev;
// 이전 노드를 마지막 노드로 지정한다.
pTail = pNode;
// 노드의 마지막은 다음 노드가 없으므로 마지막 노드의 다음 노드 가리키는 부분을 null 처리한다.
if (nullptr != pTail)
{
pTail->pNext = nullptr;
}
else
{
pHead = nullptr;
}
// 삭제 대상의 다음 노드를 반환해야 하나 삭제 대상이었던 노드는 pTail이었으므로 end iterator 을 반환하기 위해 nextIter의 pTargetNode를 null 처리 한다.
NextIter.pTargetNode = nullptr;
}
// 삭제되는 노드가 중간의 노드인 경우
else
{
// 삭제 대상 노드를 중심으로 앞 뒤 노드를 연결
_iter.pTargetNode->pPrev->pNext = _iter.pTargetNode->pNext;
_iter.pTargetNode->pNext->pPrev = _iter.pTargetNode->pPrev;
// 반환할 iterator를 삭제 대상 노드의 다음 노드를 가리키는 iterator로 대입한다.
NextIter.pTargetNode = _iter.pTargetNode->pNext;
}
// iterator가 가리키는 노드를 삭제한다.
delete _iter.pTargetNode;
// 하나의 노드가 삭제 되었으므로 노드의 개수도 하나 줄여야 한다.
--iCurCount;
return NextIter;
}
// 재귀함수로 reverse 구현
// 각 노드의 pNext 와 pPrev를 바꾼다.
template<typename T>
void CList<T>::Reverse(iterator _iter)
{
tListNode<T>* NextNode = _iter.pTargetNode->pNext;
_iter.pTargetNode->pNext = _iter.pTargetNode->pPrev;
_iter.pTargetNode->pPrev = NextNode;
iterator nextIter(this,_iter.pTargetNode->pNext);
if (nullptr == nextIter.pTargetNode)
{
return;
}
Reverse(nextIter);
}
template<typename T>
void CList<T>::reverse()
{
// 해당 리스트의 pTail 과 pHead를 바꾼다.
tListNode<T>* newHead = this->pTail;
this->pTail = this->pHead;
this->pHead = newHead;
// this의 Head를 인자로 주어야 한다.
iterator firstIter(this, pHead);
Reverse(firstIter);
}