본문 바로가기
Visual C++(MFC)

MFC10. 메모리

by 정양섭 2022. 3. 7.

  MFC를 이용하여 프로그램하기 전에는 대부분 배열 및 포인터를 이용하여 메모리를 사용하였습니다. 그리고 배열 및 포인터를 이용하여 메모리를 사용하여도 프로그램하는 데는 전혀 문제가 없습니다. 그러나 메모리 사용에 있어 더 유연한 Class를 MFC에서 제공하므로 그 내용을 여기에서 소개하고자 합니다.
  여기에서 소개하고자 하는 Class는 CArray, CList, CMap입니다. 거의 사용법은 비슷하나(조금씩 다름), 구조적으로 완전히 다른 기능을 수행합니다.

CArray
CByteArray
CDWordArray
CObArray                              //많이 사용
CPtrArray
CStringArray                         //많이 사용
CUintArray
CWordArray

CList
CPtrList
CObList                                //많이 사용
CStringList                            //많이 사용

CMap
CMapWordToPtr
CMapPtrToWord
CMapPtrToPtr
CMapWordToPtr
CMapStringToPtr
CMapStringToOb                   //많이 사용
CMapStringToString

만약 이름과 나이 주소를 20개 저장하는 메모리 공간을 만들려면 예전에는 다음과 같이 대부분 작성하였을 것입니다.

//구조체 선언
typedef struct Man{
        char name[12];
        int age;
        char address[100];
}MANDATA;

MANDATA *pManData = new MANDATA[20];    //포인터
MANDATA pManData[20];                              //배열

  여기에서 포인터와 배열의 차이점을 보면 배열의 경우 처음 할당한 메모리의 크기를 변경할 방법이 없어 필요한 최대크기를 할당해야 하고, 포인터의 경우는 필요에 따라 재할당이 가능하므로 필요한 만큼만 할당하여 사용하면 됩니다(malloc 함수로 할당, realloc로 재할당).

  혹시 학교 다닐 때 자료 구조론을 들어 본적이 있는 분이라면 Linked List라는 것을 알 것입니다. 자료를 좀더 유연하게 사용하기 위하여 구조체에 pTail이라는 포인터를 추가하여, 메모리 필요시 마다 구조체 하나씩 할당하고 pTail을 이용하여 메모리들을 연결 시키는 것, 그래서 메모리를 검색하기 위해서 앞에서부터 순서대로 pTail을 따라가면서 검색하는 것...

 

  이 Linked List를 기반으로 만들어진 Class가 CList입니다.
  Linked List의 경우 구현하기도 복잡할 뿐만 아니라, 구현하더라도 CList를 사용하는 것이 더 좋습니다. 그 이유는 CArray 및 CMap 도 같이 이점이 있는데, 메모리 할당의 최소단위로 사용되는 구조체(여기서는 MANDATA) 대신 CObject라는 Class로부터 상속 받은 Class를 사용할 수 있다는 것이다(CObArray, CObList, CMapStringToOb).
  구조체의 멤버 변수에는 정해진 크기의 변수만 들어 갈수 있는데 반해, Class에는 정해지지 않은 크기의 변수가 들어갈 수 있습니다. 구조체의 내용을 보면 알수 있지만 name 변수나 address 변수는 배열로 할당해야만 합니다. 이것 때문에 name변수에는 11문자(마지막 NULL)이상의 데이터를 입력할 수 없고, 1문자를 입력하여도 12BYTE의 메모리는 그대로 사용됩니다. 즉 여기서 설명하는 Class들을 이용하여 메모리를 할당하면 정확히 필요한 메모리를 사용하므로 구조체를 사용해서 할당하는 것 보다 효율성이 더 높게 됩니다. 또한 name 변수를 12문자로 설계하여 향후에 32문자로 늘려야 되는 경우가 있으면 여기서 설명하는 Class는 전혀 수정없이 변경이 가능하나 구조체를 사용해서 설계한 경우 많은 곳을 수정해야 합니다.
  위의 구조체 MANDATA에 해당하는 Class를 작성하면 다음과 같습니다.
//ManObject.h
class CManObject : public CObject
{
public:
        CString m_strName;
        int m_nAge;
        CString m_strAddr;
public:
        CManObject();
        CManObject &operator=(const CManObject& manObject);
};
//ManObject.cpp
#include "stdafx.h"
#include "ManObject.h"
CManObject::CManObject()
{
        m_strName = _T("");
        m_nAge = 0;
        m_strAddr = _T("");
}

CManObject &CManObject::operator=(const CManObject& manObject)
{
        m_strName = manObject.m_strName;
        m_nAge = manObject.m_nAge;
        m_strAddr = manObject.m_strAddr;
        return *this;
}

제공하는 Class를 보면 사용하는 함수는 거의 같고 다음과 같다.
        int GetCount(){ return m_ManList.GetCount();}   //List의 갯수을 얻는다.
        void Delete(CManObject *pDeleteAlarm = NULL);   //List를 삭제합니다. 인자가 NULL일 경우 전체 삭제
        BOOL Add(const CManObject& ManObject);                  //List에 항목을 추가합니다.
        BOOL Insert(const CManObject& ManObject, int nPos);     //List에 항목을 삽입합니다.
        BOOL Set(const CManObject& ManObject, int nPos);        //List의 항목중 한 항목을 수정합니다.
        CManObject* Get(int nPos);                                              //List에서 항목을 얻는다.

CMap은 순서와 상관 없으므로 Insert함수가 없습니다.
  이제 CArray, CList, CMap의 차이를 설명합니다.
  CArray와 CList는 거의 같은 기능을 수행합니다. 그러나 내부 구조는 완전히 다릅니다.
  필자의 경우 DCS 시스템을 제작하다가 경보 구조를 CList를 이용하여 작성한 적이 있습니다. 경보 데이터를 만들고 Sort를 수행할 때 가장 국민적인 Sort 기법인 Bubble Sort를 사용하여 수행하였습니다. 그러나 데이터가 많아져 Sort할 양이 많아 지니 속도가 아주 느려져서 Sort 기법을 Quick Sort로 바꾸었습니다. 그러나 속도는 여전히...
  원인은 CList에 있었다. Bubble Sort와 Quick Sort의 가장큰 차이점은 Bubble Sort가 앞에서 순자적으로 비교하여 Sort를 수행하고 Quick Sort는 반반씩 나누어 중간값과 비교하여 위치를 찾아가서 Sort를 수행하여 이미 정렬되어 있는 부분은 검색하지 않아 속도를 빨리하는 것입니다. 그러나 CList는 가운데의 데이터를 얻기 위하여 앞에서부터 순서대로 찾아가야 하니 속도개선은 없었던 것입니다. 이것을 CList 대신 CArray를 사용하여 그 문제를 해결하였습니다.
  CArray는 말그대로 배열입니다. 그러나 개선된 배열입니다. 즉 크기를 변경할 수 있는 배열로, 배열의 특성상 가운데 값을 찾기 위해서는 위치만 넣으면 바로 그 데이터를 찾을 수 있다는 것입니다.
  CArray는 어떤 위치에 상관없이 여기 저기서 데이터를 얻어서 사용해야 하는 경우 사용하고, CList는 앞에서 부터 순차적으로 데이터를 검색하여 사용할 경우 사용한다고 보면됩니다.
  CMap는 사용법은 비슷하나 전혀 다른 용도로 만들어 졌습니다. 혹시 데이터베이스 기법 중 Hash Table에 대해 알고 있다면 그 용도로 작성된 Class가 CMap입니다. 즉 데이터 작성시 내부적으로 Hash Table을 만들어 문자열(숫자, 포인터 등도 가능)을 넣으면 바로 그 데이터를 얻을 수 있는 구조로 되어 있습니다. 대량의 데이터 중 한 데이터를 얻을 때 속도를 빨리 하기 위하여 사용합니다.
참고로 만들어 놓은 Get함수는 CMap을 제외하고 잘 사용하지 않습니다. CArray의 경우 GetAt라는 함수를 직접 사용하고, CList의 경우 순차적으로 읽어야 하므로 GetHeadPosition함수와 GetNext함수를 사용하여 데이터를 얻습니다. 또한 CMap의 경우 앞에서부터 순차적으로 읽을 때 GetStartPosition함수와 GetNextAssoc함수를 사용합니다. 데모용 프로그램 참조.

데모용 프로그램
  데모용 프로그램은 단순히 홍길동과 이순신의 정보를 CArray, CList, CMap에 넣고 데이터를 얻어와 화면에 표시하는 과정을 콘솔 프로그램으로 작성하였다.

void main()
{
        CManArray manArray;
        CManList manList;
        CManMap manMap;
        CManObject manObject;

//데이타 생성
        manObject.m_strName = "홍길동";
        manObject.m_nAge = 20;
        manObject.m_strAddr = "서울 성동구 성수 2가 1동";
        manArray.Add(manObject);
        manList.Add(manObject);
        manMap.Add(manObject);

        manObject.m_strName = "이순신";
        manObject.m_nAge = 30;
        manObject.m_strAddr = "서울 광진구 구의 1동";
        manArray.Add(manObject);
        manList.Add(manObject);
        manMap.Add(manObject);

//데이타 검색   
        CManObject *pManObject;
        //Array
        for(int i = 0; i < manArray.GetCount(); i++){
                pManObject = (CManObject *)manArray.m_ManArray.GetAt(i);
                printf("Array : Name = %s, Age = %d, Addr = %s\n", pManObject->m_strName, pManObject->m_nAge, pManObject->m_strAddr);
        }

        //List
        POSITION pos;
        pos = manList.m_ManList.GetHeadPosition();
        while(pos){
                pManObject = (CManObject*)manList.m_ManList.GetNext(pos);
                printf("List : Name = %s, Age = %d, Addr = %s\n", pManObject->m_strName, pManObject->m_nAge, pManObject->m_strAddr);
        }

        //Map
        CString strKey;
        pos = manMap.m_ManMap.GetStartPosition();
        while(pos){
                manMap.m_ManMap.GetNextAssoc(pos, strKey, (CObject *&)pManObject);
                printf("Map : Name = %s, Age = %d, Addr = %s\n", pManObject->m_strName, pManObject->m_nAge, pManObject->m_strAddr);
        }
        //빠른 검색
        if(manMap.Get("홍길동", manObject))
                printf("Map.Get : Name = %s, Age = %d, Addr = %s\n", manObject.m_strName, manObject.m_nAge, manObject.m_strAddr);
}
클래스 다운로드

MemoryClass.zip
0.00MB

데모용 프로그램 다운로드

MemoryDemo.zip
0.01MB

 

'Visual C++(MFC)' 카테고리의 다른 글

MFC12. Thread  (0) 2022.03.07
MFC11. DLL 제작  (0) 2022.03.07
MFC09. 인쇄하기  (0) 2022.03.07
MFC08. View에서 깜박임 문제 해결  (0) 2022.03.07
MFC07. Doc-View 구조  (0) 2022.03.07