[질문]델파이의 TList 자료형(?)을 C/C++에서 같은 기능을 하게

dwfree74의 이미지

델파이의 TList 자료형(?)을 C/C++에서 같은 기능을 하게 하고 싶어요.

혹시 경험이 있으신 분들의 조언을 기다립니다.
저도 열심히 알아보고 있는 중입니다.

그럼 즐~프하세요..

markboy의 이미지

제 생각에는..

1. STL 을 사용한다.
2. C++ Builder에 있는 TList class를 적당히 가져온다

로 해결하시면 될 것 같습니다만.. :)

bugiii의 이미지

델파이의 TList는 C의 void* 형 컨테이너라고 생각하시면 됩니다.
델파이나 C 모두 타입, 예외 안전은 제공하지 못하므로 어쩔 수 없이 이런 방식으로 사용해야 하고, 삽입되는 메모리의 해제는 (만약 할당한 메모리라면) 프로그래머의 책임입니다. (물론 TObjectList 같은 것은 자체적인 Free를 수행해주기도 합니다만 이건 예외적인 것이겠죠.)

C++ 에서는 여러가지가 있겠지만 평범한 :wink: 해결책으로는 STL의 컨테이너를 사용하고 boost::shared_ptr 을 사용해서 여러가지 잇점을 얻을 수 있을 것입니다.

STL 컨테이너 + shared_ptr 을 한번 써보시면 돌아가기 힘들지 않을까 합니다.

frowt의 이미지

제생각에도 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;
};
dwfree74의 이미지

frowt님의 답변에 감사드립니다. :D
거의 대부분의 님들이 방법론에 대하여 리플을 올려주시는데
frowt님께서는 소스코드를 올려 주셨으니...

근데 제 내공이 딸려서 제대로 이해하기는 힘드나 아뭏든 너무 감사드립니다.

사실 저는 델파이의 TList의 기능에 대한 이해가 안된 상태이거든요.

공부를 하면서 계속 참조해 보겠습니다.

그럼 좋은 하루 되세요..^^

kldp.net 에 많은 프로그래머들이 동참하기를 바라며...^^

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.