title: "**STL.NET Primer (3/4)**"
description: "**STL.NET Primer (3/4)**"
cleanUrl: /sw-engineer/stl-net-primer-3
ogImage: ""
floatFirstTOC: right

STL.NET Primer (2/4) 에서 이어지는 글입니다.

<aside> 💡 이 컬럼은 STL.NET Primer란 제목으로 MSDN에 포스팅된 글을 번역한 것입니다.

</aside>

기반 지식 정의

STL.NET 을 알아가는 데는 두 가지 방법이 있습니다: 하나는 STL과 STL.NET의 차이점을 알아보는 것이고, 다른 하나는 STL과 STL.NET이 공통점을 알아보는 것입니다. 이 둘의 차이점을 나열하는 것은 이미 STL을 맛보았던 사람에게만 와닫을 법하기 때문에, (독한 연기로 질식시키는 듯한) 라이브러리의 낯선 부분에 대한 설명은 피하는 것이 좋을 듯 합니다. 말하자면, 컨테이너의 세밀한 특징, 그리고 System 컬렉션 라이브러리와의 상호운용 원리라는 난해한 부분에 대해서는 설명을 자제하겠다는 뜻입니다. 물론 이들 사항도 흥미로운 부분이긴 합니다. 하지만 이들 사항에 대한 설명은 이 라이브러리에 깊이 빠진 새로운 누군가의 몫으로 남겨두는 편이 더 났지 않을까요? 이것이 바로 아래에 이어질 내용 - 입문자를 위한 내용- 에 담긴 저의 의도입니다. 이런 방법을 취함으로써 이 라이브러리에 처음 발을 내딛는 사람은, STL과 STL.NET 모두가 제공하는 확장된 모델, 즉 매개변수화된(parameterized) 컬렉션에 기분좋게 다가갈 수 있을 것입니다.

그렇다면 STL과 STL.NET이 공유하는 부분은 무엇일까요? 이 둘 모두 순차(sequential) 컨테이너와 연관(associative) 컨테이너이란 두 개의 기본 컴포넌트와 지네릭 알고리즘으로 구성되어 있다는 것입니다. (맞습니다. 여러분이 STL에 익숙한 개발자라면, 어떤 내용이 이어질지 알고 있을 것입니다. 그렇다 하더라도, 이 절은 기본 용어와 기반 지식을 설명하는 데 할애되었기 때문에, 그러한 여러분의 인내심을 요청하는 바입니다.) 지네릭 알고리즘은 컨테이너 타입에 직접적으로 운용되지 않습니다. 그 대신, 이들 알고리즘에 운용할 요소의 범위를 나타내는 반복자(iterator)가 넘어가는데, 통상 이들 반복자를 가리켜 first와 last라 칭합니다. 공식적으로 좌측 포함 간격(left-inclusive interval)이라 이름붙은 아래의 요소 범위 표기법은,

// "first와 last까지의 모든 요소를 포함하지만 last는 
// 포함하지 않습니다." 라는 의미를 갖습니다.
[ first, last )

범위가 first에서 last까지지만 (first와는 달리) last는 포함되지 않음을 나타냅니다. first와 last가 같은 경우에는 그 범위 안에 어떠한 요소도 없다는 뜻입니다.

순차(sequential) 컨테이너에는 단일 타입으로 이루어진 정돈된 요소들이 담겨 있습니다. 가장 기본적인 순차 컨테이너는 vector와 list 타입입니다. (세 번째 순차 컨테이너인 - '데크'라고 읽습니다 - 는 vector처럼 동작하지만, 맨 앞 요소에 대한 효율적인 삽입과 삭제에 특화되어 있습니다. 예를 들어, 큐(queue)를 구현할 때는 vector보다는 deque가 더 났습니다.)

순차 컨테이너 타입를 참조하기에 앞서 해야할 것은, 다음의 헤더 파일 중에서 적절한 파일을 포함하는 것입니다.

#include <cli/vector>
#include <cli/list>
#include <cli/deque>

이들 헤더 파일에는 interface_vector와 같은 공유되는 기초 인터페이스의 선언부와, generic_vector등의 이들 컨테이너의 형제뻘 컨테이너도 함께 담겨 있습니다.

STL.NET 컨테이너는 참조 타입입니다. 컨테이너 선언부에는 트래킹 핸들(tracking handle)이 있는데, 이는 자동적으로 nullptr로 초기화됩니다. 그리고 우리는 gcnew 연산자를 이용하여 실제 컨테이너를 할당하게 됩니다. 이전 절에서 이미 이를 간단하게 보여주긴 했지만, 여기에 한번 더 명시하도록 하죠.

void f()
{
    // 빈 vector를 할당합니다 . . .
    vector<String^>^ = gcnew vector<String^>;

    // 기본적으로 nullptr로 각각 설정되어 있는 
    // 10개 요소가 담긴 리스트를 할당합니다.
    list<Object^>^ olist = gcnew list<Object^>( 10 );

    // 트래킹 핸들은 nullptr로 자동 설정됩니다.
    deque<int>^ ideck;

    // 뭔가 흥미로운 일을 합니다 . . .
};

연관 컨테이너에 대한 선언법과 용법에 관해서는 이 연재물의 다음 컬럼에서 다루도록 하겠습니다.

연관 컨테이너는 요소의 보관과 되찾기(retrieval)에 대한 질의(query)를 효율적으로 지원합니다. 기본적인 두 가지 연관 컨테이너 타입은 map과 set이죠. map은 key와 value 쌍으로 이루어져 있습니다. key는 검색을 위해 사용되고, value에는 저장하고 되찾아올 데이터가 담깁니다. 예를 들어, 전화번호부는 손쉽게 map을 이용하여 나타낼 수 있는데, 여기서 key는 개개의 이름을 나타내고, value는 그 이름에 연관된 전화 번호를 나타냅니다.

map은 자신의 기초를 이루는 트리 추상체(tree abstraction)을 이용하여 요소를 오름순으로 정렬합니다. hash_map 컨테이너는 되찾기(retrieval) 명령에 있어 좀더 효율적입니다. 하지만 hash_map의 반복(iteration)은 다소 임의적인 순서(random order)로 각 요소에 접근(access)합니다. 만약 되찾기가 주된 목적이라면 hash_map을 사용하는 편이 좋습니다.

set은 단일 key 값들로 구성되며, 그 값이 존재하는지에 대한 질의(query)에 효과적입니다. 예를 들어, 텍스트 질의 시스템(text query system)을 만들 경우에는 텍스트에 담긴 단어들로 이루어진 데이터베이스를 구축하기 위해선 the, and, but등과 같은 제외시킬 일반 어휘 목록이 필요할 것입니다. 이 프로그램은 텍스트에 담긴 각 단어를 차례로 읽어, 읽어낸 단어가 제외 단어 목록에 있는지를 검사하고, 질의 결과에 따라 그 단어를 데이터베이스에 저장하거나 버릴 것입니다. set뿐만 아니라, hash_set이란 컨테이너도 있는데, 이들 간의 일반적 특징은 map과 hash_map간에 보이는 일반적 특징과 동일합니다.

map과 set에 담길 각 key는 각기 서로 달라야 합니다. 하지만 multimap과 multiset은 중복된 key를 허용합니다. 예를 들어, 위의 전화번호부는 한 개인에 속한 여러 사항이 기재될 수 있어야 할 것입니다. 바로 이 경우에 multimap을 사용하면 됩니다. 이밖에도 hash_multimap과 hash_multiset이란 컨테이너가 있습니다.