C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 팁&트릭
C++Builder Programming Tip&Tricks
[861] VCL 컨테이너에 나만의 class를 담아보자.
슬픈사슴 [interest] 7661 읽음    2009-02-21 15:02
중복되는 정보인지는 일단 찻아보기는 했지만 못봤습니다. 하지만 중복정보라면 코멘트부탁해요. 삭제하겟습니다.


Builder에는 많은 유용한 컨테이너들이 많습니다.
문제는 이것들이 전부 TObject형만 담을 수 있는지라 힙에서만 생성이 됩니다. TObject를 바로 상속받은 Class로 설계하면 new로만 생성시킬수 있으므로 자동변수나, 지역변수처럼 마음대로 사용하지 못하죠.
힙에 생성하면 auto ptr등을 쓰는등 메모리 관리에 신경써야 하고 좀 귀찮은 코드가 많아집니다.

개인적으로는 클래스를 설계할 때는 플랫폼에 영향을 가급적 받지 않게 하려고 신경쓰는 편이라 플랫폼에 영향이 없는 C++고유의 별도 최상위 클래스를 설계하고 이넘을 상속받아 컨테이너에 담거나 때로는 지역변수로, 때로는 전역변수로, 때로는 자동변수로 마구잡이로 끌고 다니며 쓰고 있습니다.

빌더에서는 빌더 VCL의 훌륭한 컨테이너들도 많구요. 또한 Grid도 훌륭한 컨테이너입니다.
예를 들면 Grid의 각 Row나 Cell마다 자신의 값을 담고 있는 Object를 참조하게 넣어두면 나중에 수정이나 참조시 일일이 별도의 참조리스트를 따라다니며 찻아 다닐 필요가 없이 바로 자신의 값을 가르키는 Object를 참조할 수 있습니다.

예를 들면 그리드의 한 Row가 고객의 한개 Rocord를 가르키고 있다면 DB에서 읽어들인 고객정보를 담은 class의 참조 포인터를 Grid의 해당 Row Object에다가 담아두면 Grid에서 뭔가 변화가(값이 변하던가, 위치가 변하던가)있더라도 결과를 반영할때 해당하는 Row의 값에해당하는 것을 별도의 리스트를 찻아 다닐 필요가 없이 바로 해당하는 Row의 Object를 꺼내서 반영하면 됩니다.
(개인적인 이유로 들어오는 소스가 각가 다릅니다. 때로는 DB, 때로는 XML, 때로는 File, 해서 DB EDIT컴포넌트는 별로 사용하지 않습니다.)

문제는 VCL컨테이너들은 TObject 형밖에 담을 수 없는데 여기에 바로 사용자가 정의한 class를 담을 수 없으므로 사용자가 정의한 클래스에 TObject를 포함시켜서 사용자 정의형 클래스도 마음대로 VCL컴포넌트에 담을 수 있게 해 줄 수 있습니다.

아래는 그 소스구요.
원리는 간단합니다. 사용자 정의형 클래스 CMyObject 에 TObject형 class를 상속받은 CMyVCLObject 클래스를 맴버로 포함시키고 TObject 형 클래스가 필요하면 이 CMyVCLObject를 넘겨주면 되죠. 그리고 사용자 정의형 CMyCMyObject 의 생성자에 CMyVCLObject를 생성하고 CMyObject 파괴자에 CMyVCLObject를 삭제 해주면 메모리 신경 쓰지 않아도 됩니다.

// Head 입니다.
class CMyVCLObject;

class CMyObject
{
private:
    // 자식들이 삭제될시 처리할게 있으면 상속후 override.
    virtual void clearObject(void) {}
public:
    CMyObject(void);
    virtual __fastcall ~CMyObject(void);

public: 
    // 이 Method는 자식들이 혼자 지워보겟다고 virtual로 선언하지말것. 자식들은 필요시 위의 clearObject Method를  override해서 처리요망.
    // Template method pattern
    void Clear(void)
    {
        this->clearObject();
    }
    void setVCLObject(CMyVCLObject *pVCLObject) {this->m_pVCLObject = pVCLObject;}
    CMyVCLObject *getVCLObject(void)            {return this->m_pVCLObject;}
private:
    CMyVCLObject *m_pVCLObject;
};
//---------------------------------------------------------------------------

// VCL 컨테이너에 담기위한 TObject 형.
class  CMyVCLObject : public TObject
{
private:    // 혼자 생성금지.
    CMyVCLObject(void) {}

public:
    CMyVCLObject(CMyObject *pMyObject);
    virtual __fastcall ~CMyVCLObject(void);
    CMyObject *getObject(void) {return this->m_pObject;}
private:
    CMyObject *m_pObject;
};
//---------------------------------------------------------------------------


// ************* CPP 입니다.
CMyObject::CMyObject(void)
{
    this->m_pVCLObject = new CMyVCLObject(this);
    this->Clear();
}
//---------------------------------------------------------------------------

__fastcall CMyObject::~CMyObject(void)
{
    delete this->m_pVCLObject;
}
//---------------------------------------------------------------------------

CMyVCLObject::CMyVCLObject(CMyObject *pMyObject)
{
    this->m_pObject = pMyObject;
    this->m_pObject->setVCLObject(this);
}
//---------------------------------------------------------------------------

__fastcall CMyVCLObject::~CMyVCLObject(void)
{
    this->m_pObject->setVCLObject(NULL);
}
//---------------------------------------------------------------------------


// 사용예 입니다.

class CBank : public CMyObject
{
public:
    CBank(void) {}
    virtual __fastcall ~CBank(void) {}
    void ClearCustomList(void)
    {
        m_customList.Clear();
    }
private:
    void clearObject(void)
    {
        this->ClearCustomList();
    }
public:
    int         m_nDBIdx;
	AnsiString  m_szAccount;
	int         m_nVersion;
	unsigned    m_nDummy;
public:
    CMyObjectList m_customList;
};
//---------------------------------------------------------------------------


CMyObject class 를 상속받은 CBank class 가 위와같이 있다면,

전역변수
CBank g_Bank; // 문제 없습니다.
지역변수
bool findBank(int nCustomIdx)
{
    CBank bank;                    // 문제없습니다.
    CBank *pBank = new CBank();    // 문제없습니다.
    ....
}

pBank 형 포인터에 데이터를 담고 있다면..
VCL 컨테이너에 담기.
this->CustomGrid->Cells[0][i]      = pBank->m_szAccount;        // 그리드에 값표시.
this->CustomGrid->Objects[0][i] = pBank->getVCLObject();    // 그리드의 해당 Row에 CBank 포인터 담아두기.

VCL 컨테이너에서 꺼내기.
CMyVCLObject *pObject = dynamic_cast<CMyVCLObject *>(CustomGrid->Objects[0][i]);
CBank *pBank = dynamic_cast<CBank *>(pObject->getObject());

TObject형 컨테이너에 담아두려는 class는 전부 CMyObject 를 상속받아야 합니다. TObject 형 컨테이너에 담을 수 있는 클래스 들도 전부 TObject를 상속받아 쓰죠.
또한 간단한것이므로 복사 생성자등에 신경쓰지 않았고 가져온 값등의 유효성은 체크하지 않았으므로 이 팁의 목적은 사용방법에 있습니다. 일부 코드는 실제로 복사해서 그대로 가져 쓸 순있지만 유효성이나 버그체크는 별도로 하셔야 합니다.
필요하신분 유용하시기 바랍니다.
김태선 [cppbuilder]   2009-02-21 17:34 X
좋은 내용의 팁이네요. ^^;
VCL 을 다뤄보면 누구나 한번 쯤은 부딪히는 내용이고 누구나 한번쯤은 어떤 식으로 해결할까 고민하는 내용이죠.
좋은 해결 방법 같습니다.

그런데 참고적으로 말씀드리면, VCL에 있는 TObject * 데이타를 담을 수 있게 한 것은 편의적인 측면에서
TObject* 형을 쉽게 포인트로 담게 한 것일뿐, 어떤 형태의 포인트나 4바이트 데이타가 들어가던지 상관이 없습니다.
즉 어떤 VCL 인스턴스에 대한 포인트를 담아도 되지만,
어떤 셀에 대한 ID 성격의 int 정수 값을 담아도 상관은 없는 것입니다.

VCL 컴포넌트의 TObject 컨테이너는 범용이 목적이므로,
TObject 후손을 동적 생성해서 그 포인트를 반드시 담아야 할 이유는 없습니다.

실제 사용에 있어서도 TObject 형이라기 보다는 어떤 4바이트 포인트가 정수 값으로 다뤄지는 경우도 많습니다.
예시를 보이신 것에 기준하자면
데이타 struct 구조체 또는 클래스를 메모리에 만들고 그것에 대한 포인트를 가르키게 하는 방법이 많이 쓰입니다.

this-> 는 생략할 수 있는데 일일이 쓰시는 것을 보니 독특한 코딩 습관을 가지신 것 같습니다.
슬픈사슴 [interest]   2009-02-21 17:46 X
포인터값만만 담아두려면 위처럼 굳이 복잡하게 할 필요없이 그냥 강제Cast? 해서 담아두면 되겟죠.
하지만 VCL컨테이너중에 Remove시 Object에 할당된 메모리도 Free한다는 무서운넘이 있다는 말을 여기 Q/A어디선가 봐서 굳이 복잡하게 한것입니다. TObjectList 던가? 아마 그럴겁니다. 도움말 보면 조건부 Free 로 나오긴 합니다만 안전한게좋겟죠.

코딩시 this를 일일이 붙인건 호출하는 넘이 어디 소속인지 한눈에 보려고 그러는것입니다. this를 붙이면 최소한 직계인지 아니면 전역인지는 알 수 있겟죠. 머 namespace를 써도 되지만 그넘이나 그넘이나...

함수호출은 하는데 이넘이 도데체 어디에 있는 넘인지 헤메기를 몇번 하니 나중엔 자연히 this를 붙이게 되더군요.


+ -

관련 글 리스트
861 VCL 컨테이너에 나만의 class를 담아보자. 슬픈사슴 7661 2009/02/21
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.