새소식

언어/C++

c++ std::map 사용법 총 정리1 (생성, 반복자, 크기, 값 확인 등)

  • -

이 글은 다음 사이트를 바탕으로 작성 되었습니다.

map

이는 std::map에 있고 template class 입니다
map의 형태는 다음과 같습니다

template < class Key,                                     // map::key_type
           class T,                                       // map::mapped_type
           class Compare = less<Key>,                     // map::key_compare
           class Alloc = allocator<pair<const Key,T> >    // map::allocator_type
           > class map;

이 글은 map의 기본적인 특성을 다루기 보다는 c++에서의 map 사용법에 대해서 다루겠습니다
간단하게만 알아보자면, 맵은 특정 순서에 따라 (key, value) 조합으로 저장하는 associative container 입니다
여기서 key 값은 element를 분류하고 unique하게 값을 식별하기 위해 사용됩니다.
이때 key와 매핑된 value의 값의 type은 다를 수 있습니다
즉, <key, value> = <string, int> 와 같은 조합이 가능합니다.

map의 특징

c++ map의 특징을 정리해보겠습니다

  1. 내부의 Compare 타입의 comparison object에 의해서 key를 기준으로 정렬되어 있습니다(default 오름차순) (Ordered)
  2. 정렬되기 때문에 일반적으로 unordered_map 보다 원소에 접근하는데 속도가 느립니다.
  3. key, value의 타입이 다를 수 있습니다
  4. operator[]를 이용해 접근할 수 있습니다 (ex. map['hello'])
  5. 전형적으로 BST(Binary Search Tree)에 의해 구현됩니다.
  6. 특정 위치가 아닌 key에 의해 elements 참조된다(Associative)
  7. 어떤 두 요소도 같은 key를 가질 수 없다(Unique keys)

map 생성 및 사용

c++11 기준으로 작성하겠습니다.
총 5가지 형태의 constructor가 존재합니다
보통은 empty 형태로 생성해서 사용할 것 같긴 하지만 모두 정리해보았습니다
std::map 이므로 using namespace std를 사용하면 편리하게 사용할 수 있습니다

1. empty constructor

어떤 element도 없이 빈 상태로 생성합니다 (default constructor)
형태는 다음과 같습니다

explicit map (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
explicit map (const allocator_type& alloc);

다음과 같이 사용할 수 있습니다

map<string, int> first_map;

 

 

2. range constructor

솔직히 다른 constructor 형태는 크게 쓰이지는 않을 것 같으니 이런게 있구나만 봐도 될 것 같습니다!

범위에 따라서 많은 element들을 가진채로 map을 생성할 수 있습니다.
범위는 [first, last)
형태는 다음과 같습니다

template <class InputIterator>
  map (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& = allocator_type());

다음과 같이 사용합니다

map<string, int> second_map (first_map.begin(), first_map.end());

위와 같이 생성하게 되면 first_map에 값들이 있었을때 같은 값을 가지게 됩니다

 

3. copy constructor

map의 element들을 모두 복사하여 생성합니다
형태는 다음과 같습니다

map (const map& x);
map (const map& x, const allocator_type& alloc);

다음과 같이 사용할 수 있습니다.
즉 위 처럼 모두 복사할거면 아래처럼 모두 복사하는게 더 편하고
특정 범위만 가져오려면 위 처럼 iterator를 이용하면 될 것 같습니다.

map<string, int> third_map(seoncd);

 

4. move constructor

이는 alloc이 specified 되어있고 x의 alloc과 다른 경우 element가 이동됩니다.
라고는 쓰여있지만 언제 쓰이는지는 잘 모르겠습니다 ㅠㅠ
형태는 다음과 같습니다

map (map&& x);
map (map&& x, const allocator_type& alloc);

다음과 같이 사용할 수 있습니다

// 오름차순
struct classcomp {
    bool operator() (const string& lhs, const string& rhs) const
    {
        return lhs < rhs;
    }
};

int main() {
    map<string, int, classcomp> fourth_map;
    fourth["A"] = 1;
    foruth["C"] = 2;
    fourth["B"] = 3;

    return 0;
}

 

5. initializer list constructor

initializer list의 모든 element들을 복사합니다.
형태는 다음과 같습니다

map (initializer_list<value_type> il, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());

다음과 같이 사용할 수 있습니다

bool fncomp (string lhs, string rhs) {return lhs < rhs;}

int main() {
  const map<std::string, int> init {
    {"A", 100},
    {"B", 13},
    {"E", 11},
    {"D", 92},
  };
}

Iterator(반복자)

1. begin, end

begin: map container에서 첫 번째 element를 가리키는 iterator를 반환합니다.
end: 첫 번째가 아닌 마지막 element를 가리키는 iterator를 반환합니다.
cbegin, cend도 있지만 const 형태로 반환한다는 점만 다릅니다.
형태는 다음과 같습니다(end도 동일)

      iterator begin() noexcept;
const_iterator begin() const noexcept;

예제

map<char, int> mymap;
mymap['a'] = 100;
mymap['c'] = 200;
mymap['b'] = 99;

for (map<char,int>::iterator it = mymap.begin(); it != mymap.end(); it++) {
    cout << it->first << " => " << it->second << endl;
}

위와 같이 iterator를 반환하여 map을 순회하며 모든 key, value쌍에 접근할 수 있습니다
iterator는 포인터로 화살표(->)를 이용해서 접근할 수 있습니다.

2. rbegin, rend

rbegin: container의 마지막 요소를 가리키는 reverse iterator를 반환합니다
rend: container의 첫 번째의 바로 앞에(right before the first element) 있는 element를 가리키는 reverse iterator를 반환합니다
=> -1 번째를 가리키고 있다고 보면 될 듯??
즉 rbegin과 rend 구간은 모든 원소를 포함합니다(역순으로)
crbegin, crend도 있지만 이도 const를 반환한다는 점만 다릅니다.

형태는 다음과 같습니다

      reverse_iterator rbegin() noexcept;
const_reverse_iterator rbegin() const noexcept;

예제

    map<char, int> mymap;
    mymap['a'] = 100;
    mymap['c'] = 200;
    mymap['b'] = 99;

    map<char, int>::reverse_iterator rit;
    for (rit = mymap.rbegin(); rit != mymap.rend(); rit++) {
        cout << rit->first << " => " << rit->second << endl;
    }

begin, end의 예제와 형태는 동일하나 reverse_iterator가 쓰였고
위 처럼 하게 되면 begin, end로 순회했을때와 반대로 나오게 됩니다
즉, 내림차순 정렬로 출력이 됩니다


Capacity(용량, 크기)

Capacity와 관련된 member function은 다음 3가지가 있습니다.

1. empty

map container가 비었는지 확인합니다.
비어있다면 true, 그렇지 않다면 false를 반환합니다.

형태는 다음 처럼 간단하게 생겼습니다.

bool empty() const noexcept;

예제

map<char, int> mymap;
mymap['a'] = 100;
mymap['c'] = 200;
mymap['b'] = 99;

while (!mymap.empty()) {
    cout << mymap.begin()->first << " => " << mymap.begin()->second << endl;
    mymap.erase(mymap.begin());
}

cout << mymap.size() << endl;

위 코드는 mymap이 비어있지 않는 동안 반복하며 첫 번째 요소를 하나씩 꺼내며 출력합니다.
최종에는 mymap의 크기가 0인 것을 알 수 있습니다(그래서 while문을 탈출 함)

2. size

map container의 크기를 반환합니다

size_type size() const noexcept;

위에서 사용해봤으니 예제는 생략.

3. max_size

이는 map container가 가질 수 있는 element의 maximum size를 반환합니다
물론 이 값은 시스템 또는 라이브러리에 의해 제한된 값이지만
그 값에 도달할 수 있다는 것을 보장하지는 않는다고 합니다.(음.. 이론적으로는 그 수치이지만 사실 이론적이기 때문에 그 값에 도달하지 못하는 경우가 많으니..)
형태는 size와 같은 형태니 pass

map<int, int> mymap;
cout << mymap.max_size() << endl;    //178956970

제 컴퓨터에서 실행시에는 위 처럼 나옵니다.


Element access(value 접근)

map의 원소에 접근할 수 있는 방법입니다.

1. operator[]

대부분의 언어에서 많이 쓰이는 원소 접근 방법입니다.
python에서도 이렇게 접근했던것으로 기억합니다(dictionary)
이 전에는 pair나 iterator를 이용해 많이 접근했던것 같지만 이렇게 간단하게 접근할 수 있습니다
이 방식을 통해 값을 가져오거나 새로운 값을 넣을 수 있습니다
이 함수를 call하는 것이 아래와 같다고 합니다

(*((this->insert(make_pair(k,mapped_type()))).first)).second

예제

map<string, int> mymap;

mymap["A"] = 1;
cout << "Map Size: " << mymap.size() << endl;    //Map Size: 1

cout << mymap["C"] << endl;    //0
cout << "Map Size: " << mymap.size() << endl;    //Map Size: 2

위 에서 볼 수 있듯이 주의할 점이 있습니다.
우선 match되는 key 값이 없다면 mapped value에 대한 reference를 반환합니다
(주의)또한, mapping된 값이 element에 할당되지 않더라도 크기는 1 증가하게 됩니다

2. at

이 함수는 operator[]와 같은 일을 합니다 (c++11 이상 사용 가능)
하지만 이는 key가 존재하지 않을 시 error를 던집니다(out_of_range exception)

예제

map<string, int> mymap;

mymap["A"] = 1;

cout << "A => " << mymap.at("A") << endl;    //A => 1

이렇게 c++의 map에 대해서 알아봤습니다.
내용이 꽤 많아서 2개의 글에 나누어서 쓰려고 합니다
뭔가 되게 주저리주저림 많이 썼지만
생성하고 값 넣고 확인할 수 있게 아래만 알아도 성공이라고 생각..합니다

  • default constructor
  • operator[]
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.