본문 바로가기
개발/C++

[C++] MFC 리스트(CList/CObList/CStringList) 및 POSITION 사용법

by 램램이 2021. 9. 9.

동일한 타입의 여러 데이터들을 저장할 수 있는 구조는

STL 컨테이너를 제외하고 대표적으로 배열과 리스트가 있다.

 

이번에는 리스트의 사용법과 대표적으로 사용되는 예시에대해서 알아보려고 합니다.

MFC에서 List형태의 자료구조는 다양하게 많은데 

템플릿 리스트클래스는 CList가 있고

비 템플릿 리스트 클래스는 CObList, CStringList, CPtrList 정도가 있다.

CObList는 CObject 포인터를 데이터 타입으로 가지고,

CStringList는 CString을, CPtrList는 void 포인터를 데이터 타입으로 가진다.

 

CList를 사용하기 위해서는 "afxtempl.h" 헤더 파일을 #include 해줘야하고,

비템플릿 리스트 클래스들을 사용하기 위해서는 "afxcoll.h" 헤더를 #include 해줘야 한다.

개인적으로 가장 많이 사용하는 것은 CObList이다.

 

추가로 MFC의 CList 에 대한 자세한 정보는 MSDN을 참고하면 될 것 이다.

https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2012/bxde0zae(v=vs.110)?redirectedfrom=MSDN 

 

CList Class

CList Class 01/08/2013 2 minutes to read In this article --> Supports ordered lists of nonunique objects accessible sequentially or by value. template< class TYPE, class ARG_TYPE = const TYPE& >  class CList : public CObject Members Public Constructors Pu

docs.microsoft.com

 

MFC의 리스트 클래스들은 모두 양방향 연결 리스트로 Head와 Tail을 모두 가지고 있다.

양방향 연결리스트의 장점은 어떤 방향이로든 이동할 수 있어 원하는 위치에 편하게 접근 할 수 있다.

이제 간단한 사용법에 대해서 알아 볼건데, 가장 많이 사용되는 함수들 위주로 설명하려고 한다.

MFC 리스트의 각 항목에 대한 접근은 POSITION 타입을 사용하여 이뤄진다.

 

소스 예제

//템플릿 클래스를 사용하기 위해 헤더 추가
// ....
// 나머지 헤더는 생략
#include <afxtempl.h>

using namespace std;

CList exList;

// 리스트의 뒤쪽에 데이터 삽입
for(int i = 0; i<10 ; i++)
	exList.AddTail(i);
    
// 리스트의 앞쪽에 데이터 삽입
for(int i = 0 ; i<10 ; i++)
	exList.AddHead(i);


// 전체 출력( 데이터 삽입, 삭제 전 )
// cout << "전 : " << endl;
POSITION pos = exList.GetHeadPosition();
while(pos)
{
	int output = exList.GetNext(pos);
    cout << output << " ";
}
cout << endl;

// POSITION을 이용한 데이터 삽입
pos = exList.GetHeadPosition();
while(pos)
{
	const int five = 5;
   	int num = (int)exList.GetNext(pos);
    
    if( num == five )
    {
    	// 해당 position 기준으로 앞쪽에 100 삽입
    	exList.InsertBefore(pos, 100);
        // 해당 position 기준으로 뒤쪽에 200 삽입
        exList.InsertAfter(pos, 200);
        // 해당 Position 위치 데이터 삭제
        exList.RemoveAt(pos);
    }
}

// 전체 출력( 데이터 삽입, 삭제 후 )
// cout << "후 : " << endl;
pos = exList.GetHeadPosition();
while(pos)
{
	int output = exList.GetNext(pos);
    cout << output << " ";
}
cout << endl;

 

POSITION을 사용하여 리스트를 하나씩 GetNext(pos) 다음으로 넘겨가며 원하는 위치에 접근 할 수 있다.

양방향 연결리스트인 만큼 반대로 Next가 아닌 이전으로도 이동할 수 있는데, 이는 GetPrev(pos) 를 사용하면 된다.

 

또한 리스트의 처음 시작 위치가 중요한데, 처음부터 시작하고 싶다면 GetHeadPostion()을,

리스트의 마지막 부터 시작하고 싶다면 GetTailPosition()을 사용하고.

특정 위치에 접근하고싶다면 GetAt() 을 사용 하면된다.

 

위의 소스에서 나와있듯이 데이터의 삽입 함수는

리스트의 마지막에 데이터를 삽입하는 AddTail() 과 리스트의 앞부분에 삽입하는 AddHead() 가 있고,

특정위치에 넣고싶다면 InsertBefore()InsertAfter() 를 사용하면 된다.

 

데이터의 삭제 함수는

특정위치 삭제는 RemoveAt(), 리스트 전체 데이터를 삭제하고 싶다면 RemoveAll() 을 사용 하면된다.

 

MFC 프로젝트를 진행하면서 리스트는 정말 많이 사용 되는 구조고, 

그중에서도 CObject로 특정 템플릿 데이터를 만들어 저장 후

POSITION을 이용하여 원하는 위치를 얻어와 적절한 타입으로 형변환 하여 사용을 많이하게 된다.

 

자세히 설명하면 더 길어 지겠지만,

간략히 이정도만 알아도 어느정도 리스트를 사용하는데에는 문제가 크게 없을 것이다.

 

참고사항

GetNext()와 GetPrev() 를 사용할 때 헷갈릴 여지가 있는 부분이 존재하는데,

함수만 읽어보면 바로 다음 포인터로 넘겨주어

그 다음이나 이전부분의 노드정보를 가져올 것으로 보이지만,

위 함수들은 먼저 지금 노드의 정보를 남기고 그 후에 다음 위치로 넘어간다.

따라서 GetAt()을 사용하거나 특정 위치를 알아야 할 때

GetNext() 진행 전에 미리 저장을 해두는 방법을 사용 해야한다.

GetNext(pos) 호출 전에 GetAt(pos)해보고, GetNext(pos) 호출 후에 GetAt(pos)를 사용하여

직접 디버깅 해보면 바로 이해가 갈 것이다.

 

댓글