델파이의 TList는 C의 void* 형 컨테이너라고 생각하시면 됩니다.
델파이나 C 모두 타입, 예외 안전은 제공하지 못하므로 어쩔 수 없이 이런 방식으로 사용해야 하고, 삽입되는 메모리의 해제는 (만약 할당한 메모리라면) 프로그래머의 책임입니다. (물론 TObjectList 같은 것은 자체적인 Free를 수행해주기도 합니다만 이건 예외적인 것이겠죠.)
C++ 에서는 여러가지가 있겠지만 평범한 :wink: 해결책으로는 STL의 컨테이너를 사용하고 boost::shared_ptr 을 사용해서 여러가지 잇점을 얻을 수 있을 것입니다.
제생각에도 stl 쓰는것이 좋은 방법이라 생각되고요,
템플릿을 사용하지 않고, void* 형으로 사용하고 싶으시면,
MFC 에 CPtrList 라고 있습니다. 물론 윈도우용이고요
이걸 쪼금 고쳐서 리눅스용으로 만들어 봤습니다.
참고로, 검증이 안된 코드입니다.
PtrList.cpp
#include "afx.h" // <-- 여기엔 공통적으로 쓰이는 헤더화일을 적당히 넣어줍니다.
#include "PtrList.h"
CPlex* CPlex::Create(CPlex*& pHead, unsigned int nMax, unsigned int cbElement)
{
CPlex* p = (CPlex*) new unsigned char[sizeof(CPlex) + nMax * cbElement];
// may throw exception
p->pNext = pHead;
pHead = p; // change head (adds in reverse order for simplicity)
return p;
}
void CPlex::FreeDataChain() // free this one and links
{
CPlex* p = this;
while (p != NULL)
{
unsigned char* bytes = (unsigned char*) p;
CPlex* pNext = p->pNext;
delete[] bytes;
p = pNext;
}
}
CPtrList::CPtrList(int nBlockSize)
{
m_nCount = 0;
m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL;
m_nBlockSize = nBlockSize;
}
CPtrList::~CPtrList()
{
RemoveAll();
}
void CPtrList::RemoveAll()
{
m_nCount = 0;
m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL;
}
CPtrList::CNode*
CPtrList::NewNode(CPtrList::CNode* pPrev, CPtrList::CNode* pNext)
{
if (m_pNodeFree == NULL)
{
// add another block
CPlex* pNewBlock = CPlex::Create(m_pBlocks, m_nBlockSize,
sizeof(CNode));
// chain them into free list
CNode* pNode = (CNode*) pNewBlock->data();
// free in reverse order to make it easier to debug
pNode += m_nBlockSize - 1;
for (int i = m_nBlockSize-1; i >= 0; i--, pNode--)
{
pNode->pNext = m_pNodeFree;
m_pNodeFree = pNode;
}
}
CPtrList::CNode* pNode = m_pNodeFree;
m_pNodeFree = m_pNodeFree->pNext;
pNode->pPrev = pPrev;
pNode->pNext = pNext;
m_nCount++;
pNode->data = 0; // start with zero
return pNode;
}
void CPtrList::FreeNode(CPtrList::CNode* pNode)
{
pNode->pNext = m_pNodeFree;
m_pNodeFree = pNode;
m_nCount--;
if (m_nCount == 0)
RemoveAll();
}
/////////////////////////////////////////////////////////////////////////////
POSITION CPtrList::AddHead(void* newElement)
{
CNode* pNewNode = NewNode(NULL, m_pNodeHead);
pNewNode->data = newElement;
if (m_pNodeHead != NULL)
m_pNodeHead->pPrev = pNewNode;
else
m_pNodeTail = pNewNode;
m_pNodeHead = pNewNode;
return (POSITION) pNewNode;
}
POSITION CPtrList::AddTail(void* newElement)
{
CNode* pNewNode = NewNode(m_pNodeTail, NULL);
pNewNode->data = newElement;
if (m_pNodeTail != NULL)
m_pNodeTail->pNext = pNewNode;
else
m_pNodeHead = pNewNode;
m_pNodeTail = pNewNode;
return (POSITION) pNewNode;
}
void CPtrList::AddHead(CPtrList* pNewList)
{
POSITION pos = pNewList->GetTailPosition();
while (pos != NULL)
AddHead(pNewList->GetPrev(pos));
}
void CPtrList::AddTail(CPtrList* pNewList)
{
POSITION pos = pNewList->GetHeadPosition();
while (pos != NULL)
AddTail(pNewList->GetNext(pos));
}
void* CPtrList::RemoveHead()
{
CNode* pOldNode = m_pNodeHead;
void* returnValue = pOldNode->data;
m_pNodeHead = pOldNode->pNext;
if (m_pNodeHead != NULL)
m_pNodeHead->pPrev = NULL;
else
m_pNodeTail = NULL;
FreeNode(pOldNode);
return returnValue;
}
void* CPtrList::RemoveTail()
{
CNode* pOldNode = m_pNodeTail;
void* returnValue = pOldNode->data;
m_pNodeTail = pOldNode->pPrev;
if (m_pNodeTail != NULL)
m_pNodeTail->pNext = NULL;
else
m_pNodeHead = NULL;
FreeNode(pOldNode);
return returnValue;
}
POSITION CPtrList::InsertBefore(POSITION position, void* newElement)
{
if (position == NULL)
return AddHead(newElement); // insert before nothing -> head of the list
CNode* pOldNode = (CNode*) position;
CNode* pNewNode = NewNode(pOldNode->pPrev, pOldNode);
pNewNode->data = newElement;
if (pOldNode->pPrev != NULL)
{
pOldNode->pPrev->pNext = pNewNode;
}
else
{
m_pNodeHead = pNewNode;
}
pOldNode->pPrev = pNewNode;
return (POSITION) pNewNode;
}
POSITION CPtrList::InsertAfter(POSITION position, void* newElement)
{
if (position == NULL)
return AddTail(newElement); // insert after nothing -> tail of the list
CNode* pOldNode = (CNode*) position;
CNode* pNewNode = NewNode(pOldNode, pOldNode->pNext);
pNewNode->data = newElement;
if (pOldNode->pNext != NULL)
{
pOldNode->pNext->pPrev = pNewNode;
}
else
{
m_pNodeTail = pNewNode;
}
pOldNode->pNext = pNewNode;
return (POSITION) pNewNode;
}
void CPtrList::RemoveAt(POSITION position)
{
CNode* pOldNode = (CNode*) position;
if (pOldNode == m_pNodeHead)
{
m_pNodeHead = pOldNode->pNext;
}
else
{
pOldNode->pPrev->pNext = pOldNode->pNext;
}
if (pOldNode == m_pNodeTail)
{
m_pNodeTail = pOldNode->pPrev;
}
else
{
pOldNode->pNext->pPrev = pOldNode->pPrev;
}
FreeNode(pOldNode);
}
POSITION CPtrList::FindIndex(int nIndex) const
{
if (nIndex >= m_nCount || nIndex < 0)
return NULL; // went too far
CNode* pNode = m_pNodeHead;
while (nIndex--)
{
pNode = pNode->pNext;
}
return (POSITION) pNode;
}
POSITION CPtrList::Find(void* searchValue, POSITION startAfter) const
{
CNode* pNode = (CNode*) startAfter;
if (pNode == NULL)
{
pNode = m_pNodeHead; // start at head
}
else
{
pNode = pNode->pNext; // start after the one specified
}
for (; pNode != NULL; pNode = pNode->pNext)
if (pNode->data == searchValue)
return (POSITION) pNode;
return NULL;
}
int CPtrList::GetCount() const
{
return m_nCount;
}
bool CPtrList::IsEmpty() const
{
return m_nCount == 0;
}
void*& CPtrList::GetHead()
{
return m_pNodeHead->data;
}
void* CPtrList::GetHead() const
{
return m_pNodeHead->data;
}
void*& CPtrList::GetTail()
{
return m_pNodeTail->data;
}
void* CPtrList::GetTail() const
{
return m_pNodeTail->data;
}
POSITION CPtrList::GetHeadPosition() const
{
return (POSITION) m_pNodeHead;
}
POSITION CPtrList::GetTailPosition() const
{
return (POSITION) m_pNodeTail;
}
void*& CPtrList::GetNext(POSITION& rPosition) // return *Position++
{
CNode* pNode = (CNode*) rPosition;
rPosition = (POSITION) pNode->pNext;
return pNode->data;
}
void* CPtrList::GetNext(POSITION& rPosition) const // return *Position++
{
CNode* pNode = (CNode*) rPosition;
rPosition = (POSITION) pNode->pNext;
return pNode->data;
}
void*& CPtrList::GetPrev(POSITION& rPosition) // return *Position--
{
CNode* pNode = (CNode*) rPosition;
rPosition = (POSITION) pNode->pPrev;
return pNode->data;
}
void* CPtrList::GetPrev(POSITION& rPosition) const // return *Position--
{
CNode* pNode = (CNode*) rPosition;
rPosition = (POSITION) pNode->pPrev;
return pNode->data;
}
void*& CPtrList::GetAt(POSITION position)
{
CNode* pNode = (CNode*) position;
return pNode->data;
}
void* CPtrList::GetAt(POSITION position) const
{
CNode* pNode = (CNode*) position;
return pNode->data;
}
void CPtrList::SetAt(POSITION pos, void* newElement)
{
CNode* pNode = (CNode*) pos;
pNode->data = newElement;
}
PtrList.h
// abstract iteration position
struct __POSITION { };
typedef __POSITION* POSITION;
struct CPlex // warning variable length structure
{
CPlex* pNext;
#if (_AFX_PACKING >= 8)
DWORD dwReserved[1]; // align on 8 byte boundary
#endif
// BYTE data[maxNum*elementSize];
void* data() { return this+1; }
static CPlex* Create(CPlex*& head, unsigned int nMax, unsigned int cbElement);
// like 'calloc' but no zero fill
// may throw memory exceptions
void FreeDataChain(); // free this one and links
};
class CPtrList
{
protected:
struct CNode
{
CNode* pNext;
CNode* pPrev;
void* data;
};
public:
// Construction
CPtrList(int nBlockSize = 10);
~CPtrList();
// Attributes (head and tail)
// count of elements
int GetCount() const;
bool IsEmpty() const;
// peek at head or tail
void*& GetHead();
void* GetHead() const;
void*& GetTail();
void* GetTail() const;
// Operations
// get head or tail (and remove it) - don't call on empty list!
void* RemoveHead();
void* RemoveTail();
// add before head or after tail
POSITION AddHead(void* newElement);
POSITION AddTail(void* newElement);
// add another list of elements before head or after tail
void AddHead(CPtrList* pNewList);
void AddTail(CPtrList* pNewList);
// remove all elements
void RemoveAll();
// iteration
POSITION GetHeadPosition() const;
POSITION GetTailPosition() const;
void*& GetNext(POSITION& rPosition); // return *Position++
void* GetNext(POSITION& rPosition) const; // return *Position++
void*& GetPrev(POSITION& rPosition); // return *Position--
void* GetPrev(POSITION& rPosition) const; // return *Position--
// getting/modifying an element at a given position
void*& GetAt(POSITION position);
void* GetAt(POSITION position) const;
void SetAt(POSITION pos, void* newElement);
void RemoveAt(POSITION position);
// inserting before or after a given position
POSITION InsertBefore(POSITION position, void* newElement);
POSITION InsertAfter(POSITION position, void* newElement);
// helper functions (note: O(n) speed)
POSITION Find(void* searchValue, POSITION startAfter = NULL) const;
// defaults to starting at the HEAD
// return NULL if not found
POSITION FindIndex(int nIndex) const;
// get the 'nIndex'th element (may return NULL)
// Implementation
protected:
CNode* m_pNodeHead;
CNode* m_pNodeTail;
int m_nCount;
CNode* m_pNodeFree;
struct CPlex* m_pBlocks;
int m_nBlockSize;
CNode* NewNode(CNode*, CNode*);
void FreeNode(CNode*);
public:
// local typedefs for class templates
typedef void* BASE_TYPE;
typedef void* BASE_ARG_TYPE;
};
제 생각에는..1. STL 을 사용한다.2. C++ Build
제 생각에는..
1. STL 을 사용한다.
2. C++ Builder에 있는 TList class를 적당히 가져온다
로 해결하시면 될 것 같습니다만.. :)
델파이의 TList는 C의 void* 형 컨테이너라고 생각하시면 됩니다.
델파이의 TList는 C의 void* 형 컨테이너라고 생각하시면 됩니다.
델파이나 C 모두 타입, 예외 안전은 제공하지 못하므로 어쩔 수 없이 이런 방식으로 사용해야 하고, 삽입되는 메모리의 해제는 (만약 할당한 메모리라면) 프로그래머의 책임입니다. (물론 TObjectList 같은 것은 자체적인 Free를 수행해주기도 합니다만 이건 예외적인 것이겠죠.)
C++ 에서는 여러가지가 있겠지만 평범한 :wink: 해결책으로는 STL의 컨테이너를 사용하고 boost::shared_ptr 을 사용해서 여러가지 잇점을 얻을 수 있을 것입니다.
STL 컨테이너 + shared_ptr 을 한번 써보시면 돌아가기 힘들지 않을까 합니다.
CPtrList
제생각에도 stl 쓰는것이 좋은 방법이라 생각되고요,
템플릿을 사용하지 않고, void* 형으로 사용하고 싶으시면,
MFC 에 CPtrList 라고 있습니다. 물론 윈도우용이고요
이걸 쪼금 고쳐서 리눅스용으로 만들어 봤습니다.
참고로, 검증이 안된 코드입니다.
PtrList.cpp
PtrList.h
frowt님의 답변에 감사드립니다. :D 거의 대부분의 님들이 방법론
frowt님의 답변에 감사드립니다. :D
거의 대부분의 님들이 방법론에 대하여 리플을 올려주시는데
frowt님께서는 소스코드를 올려 주셨으니...
근데 제 내공이 딸려서 제대로 이해하기는 힘드나 아뭏든 너무 감사드립니다.
사실 저는 델파이의 TList의 기능에 대한 이해가 안된 상태이거든요.
공부를 하면서 계속 참조해 보겠습니다.
그럼 좋은 하루 되세요..^^
kldp.net 에 많은 프로그래머들이 동참하기를 바라며...^^
댓글 달기