리스트는 클래스로, 노드는 구조체로 만들기로 한다.
데이터가 들어오는 게 노드니까 노드를 템플릿으로 만든다.
-> 구조체 템플릿
그럼 tListNode 는 구조체 템플릿의 이름이 된다.
리스트 입장에서는, 구조체를 가리키는 포인터 타입이 멤버로 있으므로(m_pHeadNode) 타입을 명확하게 정해줄 필요가 있다.
( <T> 자리에 뭐가 들어갈지 알려줘야 함 )
-> 그럼 리스트도 아직 데이터가 안 정해진 템플릿이 됨.
-> 클래스 템플릿
-> 리스트의 타입 T를 정해주면 연쇄적으로 구조체의 타입 T까지 정해진다.
메인에서 float 타입을 받는 리스트 생성
-> CList<float> list;
코드
//CList.h
template<typename T>
struct tListNode
{
T data;
tListNode* pNext;
};
template<typename T>
class CList
{
private:
tListNode<T>* m_pHeadNode;
int m_iCount;
};
양방향 리스트로 만들기
-> tListNode 는 구조체가 아니라 구조체 템플릿이다.
따라서 (본인이랑 똑같은) 다음 노드를 가리키는 포인터를 선언하려면 tListNode<T>* 와 같이 작성해야 한다.
(tListNode<T> 까지가 구조체 이름임.)
-> But 구조체 선언 시 자기 자신을 tListNode로 지칭해도, tListNode<T> 를 의미한다는 것으로 이해하기 때문에 굳이 타입네임까지 적지 않아도 됨.
반면에 이 구조체를 실제로 사용하는 리스트 쪽에서는
tListNode<T> 로 적지 않으면 문제가 된다.
(자기 자신을 지칭하는 것이 아니기 때문에)
생성자/소멸자 선언
-> 생성자 : 리스트 초기화, 소멸자 : 데이터 할당해제
push_back() 함수 템플릿
template<typename T>
void CList<T>::push_back(const T& _data)
const T& 인 이유
-> 원본은 수정 X, 참조만 받아오기 위해
tListNode()tListNode(const T& _data, tListNode<T>* _pPrev, tListNode<T>* _pNext)tListNode<T>* pNewNode = new tListNode<T>(_data, nullptr, nullptr);-> 방금 만들어졌기 때문에 _pPrev, _pNext는 어차피 널포인터 상태push_front() 함수 템플릿
template<typename T>
void CList<T>::push_front(const T& _data)
tListNode<T>* pNewNode = new tListNode<T>(_data, nullptr, m_pHead);_pPrev 는 널포인터_pNext엔 리스트가 원래 가리키고 있던 헤드의 주소값(m_pHead)을 넣어준다. 그럼 새 노드가 이 기존의 헤드 노드를 가리키게 된다.코드
#pragma once
template<typename T>
struct tListNode
{
T data;
tListNode<T>* pPrev;
tListNode<T>* pNext;
tListNode()
: data()
, pPrev(nullptr)
, pNext(nullptr)
{
}
tListNode(const T& _data, tListNode<T>* _pPrev, tListNode<T>* _pNext)
: data(_data)
, pPrev(_pPrev)
, pNext(_pNext)
{
}
};
template<typename T>
class CList
{
private:
tListNode<T>* m_pHead;
tListNode<T>* m_pTail;
int m_iCount;
public:
void push_back(const T& _data);
void push_front(const T& _data);
public:
CList();
~CList();
};
template<typename T>
void CList<T>::push_back(const T& _data)
{
// 입력된 데이터를 저장할 노드를 동적할당
tListNode<T>* pNewNode = new tListNode<T>(_data, nullptr, nullptr);
if (nullptr == m_pHead)
{
// 처음 입력된 데이터라면
m_pHead = pNewNode;
m_pTail = pNewNode;
}
else
{
// 데이터가 1개 이상일 때 입력된 경우
// 현재 가장 마지막 데이터(tail)를 저장하고 있는 노드와
// 새로 생성된 노드가 서로 가리키게 한다.
m_pTail->pNext = pNewNode;
pNewNode->pPrev = m_pTail;
// List가 마지막 노드의 주소값을 새로 입력된 노드로 갱신한다.
m_pTail = pNewNode;
}
// 데이터 개수 증가
++m_iCount;
}
template<typename T>
void CList<T>::push_front(const T& _data)
{
// 새로 생성된 노드의 다음을 현재 헤드노드의 주소값으로 채움
tListNode<T>* pNewNode = new tListNode<T>(_data, nullptr, m_pHead);
// 현재 헤드노드의 이전노드 주소값을 새로 생성된 노드의 주소로 채움
m_pHead->pPrev = pNewNode;
// 리스트가 새로 생성된 노드의 주소를 새로운 헤드주소로 갱신한다.
m_pHead = pNewNode;
//데이터 개수 증가
++m_iCount;
}
template<typename T>
CList<T>::CList()
: m_pHead(nullptr)
, m_pTail(nullptr)
, m_iCount(0)
{
}
template<typename T>
CList<T>::~CList()
{
tListNode<T>* pDeleteNode = m_pHead;
while (pDeleteNode)
{
tListNode<T>* pNext = pDeleteNode->pNext;
delete(pDeleteNode);
pDeleteNode = pNext;
}
}