C++ STL

2021. 8. 14. 21:33C++

728x90

// 표준 템플릿 라이브러리(STL, Standard Template Library)

- 일반화 프로그래밍(Generic Programming)을 기반으로 하는 한다.

- 자료구조와 알고리즘을 중시

※ 일반화 프로그래밍 : 데이터를 중시하는 객체 지향 프로그래밍과 다르게,

프로그램의 알고리즘에 중점을 두는 방식

※ 템플릿 : 매개변수의 타입에 따라 함수나 클래스를 생성하는 매커니즘

타입이 전달받는 매개변수의 형태에 따라 자동 변형되어 처리된다.

<STL의 3요소>

1. 반복자 (iterator) : 컨테이너에 저장된 요소를 반복적으로 순회하여

각 요소에 대한 접근을 도와준다.

2. 컨테이너 (container) : 같은 타입의 여러 객체를 저장하는 집합.

3. 알고리즘 (algorithm)

==============================================

//==============================================

// 동적 배열 클래스 std::vector

/*

1. 개요

- 일반 배열과 동일하게 []연산자로 값을 참조 & 대입 가능

- 배열의 크기(사이즈)와 데이터의 삽입(push back / insert) & 삭제(erase / pop back)도 비교적 쉽게 실행할 수 있다.

- C++에서는 vector 이외에도 복수의 데이터를 취급하는 클래스(std::list)가 몇개 존재하나, vector가 가장 빠르고 메모리 효율도 좋다.

2. 동적 배열 (dynamic array)

- 기존의 배열은 일반 변수보다 사용빈도도 높고 편리한 데이터 타입이지만, 배열의 크기를 미리 지정해야만 한다. 또한, 프로그램 실행 중에

변경 할 수도 없음.

- 반면 동적 배열은 그러한 상황에서 비교적 자유자재로 변화시킬 수 있어 기존의 배열보다 효율이 뛰어나다.

*/

// std::vector의 선언

/*

- vector의 선언은 여러 가지가 있다.

(빈 벡터 선언, 사이즈 지정 선언, 사이즈/데이터 지정 선언, 특정 데이터 기반 선언 등등)

- 특정 위치에서 선언하고 다른 곳으로 이동했을 때, 만약 그곳이 스코프

(scope, 유효범위)를 벗어난 곳이라면 해당 vector는 자동 소멸.

1) 초간단 선언하기

- vector 객체를 만들기 위해서는 다음과 같은 문법을 쓴다.

[예시]

std::vector<타입> 객체명;

- 데이터 타입은 일반적인 타입부터 구조체, 클래스도 가능하다.

- 이렇게 선언한 vector의 요소 수는 0;

2) 요소 수를 지정하여 선언하기

- 동적 배열은 프로그램 실행 중에 사이즈를 변화시킬 수 있다.

- 하지만 데이터 영역의 확보와 파기, 그리고 복사 처리를 해야하므로

약간의 처리 시간을 필요로 한다.

- 때문에 요소 수를 알고 있으면 되도록 그 수를 지정하여 객체로 만드는 것이 유리하다.

[예시]

std::vector<int> data(123);

-> int형 벡터 data의 요소 수는 123이다.

- 배열에서는 요소 개수를 표현할 때 [숫자]로 표현하지만,

벡터에서는 (숫자)로 표현한다.

*/

// std::vector의 초기화

/*

- 요소 수를 지정하는 것 뿐만 아니라 요소 전체의 값을 지정해 초기화하는 것도 가능.

=================================================

[예시]

std::vector<int> data(10, 5);

->요소 개소가 10개이고 그 요소의 값이 모두 5로 초기화된다.

- 지정한 숫자만큼 사이즈를 확보하고, 지정된 값으로 전부 초기화를 실행한다.

- 하지만 이 방법으로 값이 완전히 동일한 경우가 아니면 초기화가 불가능,

=================================================

[예시]

std::vector<type> name(itr* first, itr* last);

-> 일반 배열 데이터 가져가 쓸때 사용

- 위 예시는 first와 last가 가리키는 데이터를 기반으로 초기화한다.

- 이 형태를 이용하여 일반 배열 데이터를 기반으로 동적 배열 구축이 가능하다.

=================================================

[예시]

int _data[]{4,5,6};

vector<int> data(_data, _data + 3);

- 만약 기반 데이터 배열 사이즈가 변경 되면 필히 바꿔야하므로 위험하다.

- 이것을 사전 차단하기 위해 C++11부터 std::begin(배열명), std::end(배열명) 함수를 이용하여 배열을 포인터로 반환한다.

=================================================

[예시]

int _data[]{4,5,6};

vector<int> data(std::begin(_data), std::end(_data));

- 이외에도 C++11부터 vector를 초기화 할 때, 초기값을 직접 지정할 수 있다.

[예시]

std::vector<int> data{4, 5, 6};

=================================================

*/

// iterator

/*

- vector, list, map 등의 컨테이너 클래스에는 iterator 라는 것이 들어 있다.

- iterator는 추상화된 포인터를 가리킨다. (추상화 : 어떤 대상의 특징을 요점만 추려내는 것)

- * 연산자로 iterator가 가리키는 특정 요소를 참조, 변경 할 수 있다.

- std::vector<T>의 iterator는 vector<T>::iterator 이다.

[예시]

std::vector<int>::iterator itr;

-> vector<int>의 요소에 접근하기 위한 iterator

- begin() 멤버 함수는 첫 번째 요소를 반환한다.

- end() 멤버 함수는 마지막 요소를 하나 더 지나친 위치의 iterator를 반환한다.

유효한 객체를 가리키지 않으므로 역참조나 증감이 불가능하다.

- 이를 사용하여 iterator를 초기화하고 매개체로써 활용가능.

=================================================

[예시]

vector<int> v{ 1,2,3,4 };

vector<int>::iterator itr = v.begin(); // 첫번째 요소를 반환하는 begin

cout << *itr << endl; // 첫번째 요소를 참조하여 출력

++itr; // 다음 요소로 이동

*itr = 9; // 두 번째 요소의 값을 2에서 9로 변경

- iterator는 auto 키워드도 인식한다.

- 일일이 타이핑하는 건 어려우니 auto 활용

[예시]

vector<int> v{ 1,2,3,4 };

auto itr = v.begin(); // 첫번째 요소를 반환하는 begin

cout << *itr << endl; // 첫번째 요소를 참조하여 출력

=================================================

- iterator를 사용하여 vector v의 요소를 모두 검사할 때는 아래와 같은 방법을 사용한다.

[예시]

for(auto itr = v.begin(); itr !=v.end(); ++itr)

{

cout << *iter << endl;

}

=================================================

*/

// std::vector의 데이터 삽입

/*

1. push_back()

- 데이터를 vector의 요소 맨 끝에 배치하는 멤버 함수.

- 보통 데이터의 추가는 이 멤버 함수만을 사용한다.

[예시]

std::vector<int> v;

v.push_back(123);

- 처리 시간이 O(1) ( = 데이터 수에 관계없이 항상 일정속도로 처리)

O(1)

O(log N)

O(N)

2. insert(iterator, value)

- 임의의 위치에 데이터를 삽입하고 싶은 경우 사용하는 멤버 함수.

- 제 1인수에는 삽입하고 싶은 vector의 위치를, 제 2인수에는 삽입할 데이터를 지정

- n번째에 삽입하고 싶을 경우엔 객체명.begin() + n 라고 기억하면 됨

- push_back()에 비해 처리시간(O(N))이 상당량 소모되므로 권장하진 않음.

[예시]

std::vector<int> v(10, 3); // 값이 3이고, 개수는 10개

v.insert(v.begin() + 4, 7); // [4] 위치에 7을 대입

- 배열 중간에 빈번한 값의 삽입/삭제를 실행할 경우에는 std::list나 std::gap_vector의 사용을 검토하는 편이 좋다.

- 데이터의 가장 앞부분에 빈번한 삽입/삭제를 하는 경우 std::deque가 좋다

- 하지만 데이터 수가 10개 내외일 정도로 작을 경우엔 vector가 좋다

*/

// std::vector의 데이터 삭제

/*

1. pop_back()

- vector의 맨 끝에 위치한 데이터를 삭제하는 멤버 함수

- 처리속도가 push_back()과 동일한 O(1) 이다.

예시) std::vector<int> v{3, 1, 4, 1, 5};

v.pop_back(); // 맨 마지막의 데이터를 삭제한다.

- 빈 vector에서 pop_back()을 실행하면 디버그에서 예외가 발생한다.

이를 방지하기 위해 empty() 혹은 size()로 vector의 내부 크기를 확인해야 한다.

2. erase(iterator)

- 임의의 위치에 있는 데이터를 삭제하고 싶은 경우 사용하는 멤버 함수.

- 중간의 요소를 삭제하면 사이즈가 1 감소하여 삭제된 요소 다음에 위치해있던 요소들이 자리 이동을 실시한다.

- 그러나 요소 수가 많을 경우 데이터 이동에 시간을 필요로 하니 주의

예시) std::vector<int> v{3, 1, 9, 4};

v.erase(v.begin() + 2); // 3번째의 요소 (9)를 삭제

3. erase(first, last)

- vector의 일부를 삭제하고 싶은 경우 하나하나 지우지 않아도 범위 삭제가 가능

예시) std::vector<int> v{3, 1, 4, 1, 5};

v.erase(v.begin() + 1, v.begin() + 3); // 1, 4를 삭제

*/

// std::vector의 대입

/*

1. 대입 연산자

- 동일 타입의 vector라면 = 연산자를 사용하여 일반 변수와 동일하게 값을 대입할 수 있다.

예시) vector<int> a{1, 2, 3, 4};

vector<int> b{9, 8};

b= a; // a의 요소를 b에 대입

-> 둘이 합쳐지는게 아닌 완전히 값이 바뀐다.

2. assign 함수

- assign(first, last)는 특정 범위의 데이터를 vector에 대입한다.

예시) int ar[] {1, 2, 3, 4, 5, 6, 7};

vector<int> v{9, 8};

v.assign(&ar[0], &ar[3]); // v의 내부 값은 {1, 2, 3}으로 변화

- assign(size, 값)을 하면 요소를 범위 지정하여 대입할 수 있다.

- resize(size, 값)도 의미상 비슷하긴 하나 resize()는 처음부터 존재하던 요소를 삭제하는 것이 아니기 때문에

특정 값으로 완전 초기화를 하고 싶으면 이걸 써야한다.

vector<int> v{9, 8}

v.assign(3, 1); // 사이즈가 3이고 그 값이 전부 1임 / v의 내부 값은 {1, 1, 1}로 변화

*/

// std::vector의 참조

/*

1. [] 연산자

- 일반적인 배열과 동일하게 인덱스를 사용하여 배열 요소 값을 참조, 대입할 수 있다.

- [] 연산자를 사용한 랜덤 엑세스 소요시간은 O(1)이다.

이는 일반 배열과 마찬가지일 정도로 매우 빠른속도를 의미

[예시1]

std::vector<int> data{3, 1, 4, 1, 5, 9, 2, 6, 5, 3};

for(int i = 0; i<10; i++)

std::cout << data[i]; // data의 i번쨰 요소를 표시

[예시2]

const int SZ= 10; // 요소개수

std::vector<int>v(SZ); // 지정된 요소 수를 가지는 vector 생성

for(int i=0; i< SZ; ++i)

v[i] = i; // 요소를 0, 1, 2, 3,....9의 값으로 설정

2. at함수

- i번째 데이터에 엑세스하게 만들어주는 멤버 함수

- at() 함수는 값이 아닌 참조를 반환하므로 [] 연산자처럼 값을 참조하는 것과 대입하는 것이 모두 가능하다.

[예시1]

std::vector<int> data{3, 1, 4, 1, 5, 9, 2, 6, 5, 3};

for(int i = 0; i<10; i++)

std::cout << data.at(i); // data의 i번쨰 요소를 표시

[예시2]

const int SZ= 10; // 요소개수

std::vector<int>v(SZ); // 지정된 요소 수를 가지는 vector 생성

for(int i=0; i< SZ; ++i)

v.at(i) = i; // 요소를 0, 1, 2, 3,....9의 값으로 설정

*/

// 기타 멤버 함수

/*

1. vector의 상태를 받아오는 멤버 함수

1) empty()

- vector가 비어있는 상태인지 아닌지 검사하는 함수

- 비어있으면 true, 아니면 false를 반환

if(v.empty())

{

v가 비어있을 경우 처리를 실행하도록

}

2) size()

- vector의 요소 개수를 반환하는 함수

for(int i= 0; i != v.size(); i++)

{

벡터 전체를 순회하라

}

2. vector의 요소를 받아오는 멤버 함수

1) front() : 맨 앞의 요소를 참조반환. 객체명[0]와 동일.

2) back() : 맨 뒤의 요소를 참조반환. 객체명[객체명.size() -1]과 동일

// 아래와 같이 대입도 가능

std::vector<int> a{1, 2, 3, 4};

v.front() = 10;

-> 나중에 확인해보자

3. vector의 메모리 멤버 함수

1) clear()

- vector를 텅 비게 만들어 버리는 함수.

- size가 0으로 바뀔 뿐, 할당된 메모리가 해제 되지는 않는다.

- 메모리를 해제하고 싶은 경우 shrink_to_fit(), swap()을 실행

2) resize(리사이즈)

- 요소 수를 지정한 값만큼으로 설정하는 함수

- 만약 사이즈가 capacity를 초과하는 경우 메모리가 새로 할당되어 데이터를 복사한다.

- 사이즈가 늘어난 부분의 값을 별도로 지정하고 싶다면 resize(사이즈, 값)처럼 두번째

인수를 지정해줘야한다.

- clear()와 마찬가지로 사이즈를 바꿀 수 있을 뿐, 메모리를 해제할 수는 없다.

- 메모리를 해제하고 싶은 경우 shrink_to_fit(), swap()을 실행

3) swap(객체)

- 인수로 삽입한 객체와 내부 요소를 서로 바꾸는 함수.

std::vector<int> x, y;

x.swap(y);

-> x와 y의 값이 뒤바뀐다. 말그대로 스왑해버림

4) shrink_to_fit()

- C++11 이전에 clear()를 사용하여 해제가 되지 않는 메모리를 해제할 때?

std::vector<int> v;

std::vector<int>().swap(v); // 텅빈 임시 객체 생성 후 swap 사용

- 하지만 C++11부터 shrink_to_fit() 함수가 추가되었다.

- capacity를 현재 vector의 사이즈로 변경하고, 남은 잉여 메모리를 해제.

*/

// 알고리즘 함수

/*

1. count()

- count(객체명.begin(), 객체명.end(), value)

- 컨테이너의 처음부터 끝까지 검사한 후, value의 개수를 정수 형으로 반환한다.

[예시]

vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

cout << "요소 7의 개수 : " << count(v.begin(), v.end(), 7) << endl;

// 객체도 셀수있다. ex) 클래스

2. find()

- find(객체명.begin(), 객체명.end(), value)

- 컨테이너의 처음부터 끝까지 검사한 후, value가 중복이 될 때

가장 처음에 위치한 값을 반환한다.

- 지정 valuㄷ를 찾지 못할 경우에는 객체.end()를 반환한다. // 사실상 null

[예시1]

vector<int> v{ 1,2,3,1,2,3,1,2,3 };

auto itr = std::find(v.begin(), v.end(), 2);

cout << *itr << endl;

[예시2]

vector<int> y{ 1,2,3,4,5,6,7,8,9,10 };

auto itr = std::find(y.begin(), y.end(), 15);

if (itr == y.end())

{

cout << "초과 범위 입력" << endl;

}

else

{

cout << *itr << endl;

}

3. accumulate()

- accumulate(객체명.begin(), 객체명.end(), init)

- 컨테이너의 처음부터 끝까지 검사한 후, 내부에 존재하는 요소를 전부 합산하다.

- 합산된 값은 세번째 인수인 initial value를 따라간다 (보통 0을 기입)

- 사용하기 위해서 #include <numeric> 선언이 필요하다

[예시]

#include <numeric>

...

vector<int> v {1,2,3,4,5,6,7,8,9,10}

cout << accumulate(v.begin(), v.end(), 0) << endl;

4. sort()

- sort(객체명.begin(), 객체명.end())

- 컨테이너의 내부 요소를 오름차순으로 정렬한다.

- 단, 사용하려면 #include <algorithm> 선언을 해야한다

[예시]

vector<int> v{ 4,5,1,9,8,2,3,6,7 };

sort(v.begin(), v.end()); // 전체 데이터 정렬

sort(v.begin() + 1, v.end() - 1); // 일부 데이터 정렬

sort(&v[1], &v[5]); // 포인터(주소)로 정렬 위치 지정

5. reverse()

- reverse(객체명.begin(), 객체명.end())

- 데이터 정렬 순서를 역순으로 만들어 주는 함수

[예시]

vector<int> v{ 4,5,1,9,8,2,3,6,7 };

reverse(v.begin(), v.end());

for(auto a:v)

cout << a << endl;

*/

//==============================================

//==============================================

// 양방향 연결 리스트 클래스 std::list

/*

1. 양방향 연결 리스트 (doubly linked list)

- 요소를 보관하는 각각의 노드에 앞뒤로 위치한 노드의 포인터가 존재

- 이 때문에 어느 위치에 삽입/삭제를 실행해도 O(1)의 속도로 처리할 수 있음

2. vector와의 차이점

1) std::vector

- 맨 마지막 위치에서 실시하는 데이터 삽입/삭제 속도는 O(1)

- 그 외의 위치에서 실시하는 데이터 삽입/삭제 속도는 O(N) -> 배열형식이기 때문

- 어떤 위치에서든 O(1)의 속도로 랜덤(고속) 액세스(참조와 대입)가 가능하다.

- 배열과 비슷하게 연속된 메모리 할당 구조

2) std::list

- 데이터 위치에 상관없이 모든 삽입/삭제가 O(1)로 동작

- n번째에 위치한 데이터로 이동할 때, 선두와 후미 또는 현재 위치에서 포인터를 받아야

하므로 랜덤 액세스 속도가 O(N)이다.

- 각각의 노드가 독립적인 메모리를 갖고 있고, 이것이 포인터로 상호 링크 되어 있음

*/

// std::list의 선언

/*

1) 초간단 선언하기

- #include <list> 선언이 선행 조건

- list 객체를 만들기 위해서는 아래와 같은 문법 사용

[문법]

std::list<타입> 객체명;

2) 요소 수를 지정하여 선언하기

- vector는 데이터 영역이 연속되어 있어, 공간이 부족한 경우 메모리의

추가 할당과 데이터 복사가 필연적이다

- 반면 list는 데이터 영역이 모두 독립적이기 때문에 굳이 사이즈를 지정하여

객체를 만들어도 vector에 비해 이렇다할 메리트가 없다.

[예시]

std::list<int> data(123);

-> int형에 초기 요소 수가 123인 리스트 data를 선언

*/

// std::list의 초기화는 vector의 초기화와 같다

// std::list의 iterator

/*

1. 개요

- list에는 [] 연산자가 없다. 따라서 n번째 요소 참조하는 것이 불가능

- 때문에 임의의 위치에 저장된 데이터를 탐색하기 위해 iterator를 사용한다.

2. 상세

- begin() : 제일 처음에 위치한 데이터의 iterator를 반환한다.

- end() : 제일 마지막에 위치한 데이터의 다음 위치에 해당하는 iterator를 반환한다.

유효 객체를 가리키지 않아 역참조같은 것이 불가능하다.

- 증감 연산자(++/--)로 iterator를 다음으로 넘기거나 되돌아갈 수 있다.

- * 연산자로 iterator가 가리키는 요소를 참조 할 수 있다.

*/

// std::list의 데이터 삽입

/*

1. front_back()

- 데이터를 list 내부의 맨 앞에 배치하는 멤버 함수

- 처리 시간이 O(1)으로 요소 수에 관계없이 항상 일정시간으로 처리

[예시]

std::list<int> v;

v.push_front(456);

2. push_back()

- 데이터를 list의 요소 맨 끝에 배치하는 멤버 함수.

- 처리 시간이 O(1)으로 요소 수에 관계없이 항상 일정시간으로 처리

[예시]

std::list<int> v;

v.push_back(123);

3. insert(iterator, value)

- 임의의 위치에 데이터를 삽입하고 싶은 경우 사용하는 멤버 함수.

- 제1인수에는 삽입하고 싶은 list의 위치를, 제2인수에는 삽입할 데이터를 지정

[예시]

list<int> v{ 1, 2, 3, 4, 5, 6, 7 };

for (auto itr = v.begin(); itr != v.end(); itr++)

{

if (*itr == 3)

{

v.insert(itr, 10); // 3앞에 10을 삽입, itr는 10을 가리킨다.

++itr; // itr을 3의 위치로 이동

}

}

for (auto a : v)

{

cout << a << endl;

}

*/

// std::list의 데이터 삭제

/*

1. pop_front()

- list 내부의 맨 앞에 위치한 데이터를 삭제하는 멤버 함수.

- 처리시간이 O(1)으로 요소 수에 관계없이 항상 일정시간 처리

[예시]

std::list<int> v{ 1, 2, 3, 4, 5 };

v.pop_front();

2. pop_back()

- list 내부의 맨 뒤에 위치한 데이터를 삭제하는 멤버 함수.

- 처리시간이 O(1)으로 요소 수에 관계없이 항상 일정시간 처리

[예시]

std::list<int> v{ 1, 2, 3, 4, 5 };

v.pop_back();

3. erase

- 임의의 위치에 있는 데이터를 삭제하고 싶은 경우 사용하는 멤버 함수

[예시]

std::list<int> v{ 1, 2, 3, 4, 5 };

auto itr = lst.begin();

++itr;

++itr; // 세번째 위치로 이동

lst.erase(itr); // 세번째 위치의 데이터인 3을 삭제

- vector 처럼 삭제된 위치에서 자리이동이 일어나지 않으면, 포인터가 바뀌는 것이 전부

- itr가 가리키고있던 메모리 주소는 힙 영역으로 반환되는 동시에 itr가 가리키고 있던 곳은

무표 판정으로 변하므로 그대로 사용하면 안된다.

- 이후에도 itr를 계속 사용한다면 반드시 erase(itr)이 반환되는 값을 itr에 대입하고 itr이

노드를 올바르게 가리킬 수 있도록 해야만 한다.

[예시]

리스트에 포함된 데이터 중에서 1을 모두 지우고 싶은 경우

list<int> lst {1, 2, 3, 4, 5, 1, 6, 7, 8, 9, 1, 10 };

for(auto itr = lst.begin(); itr != lst.end(); itr+=)

{

if(*itr ==1) // 데이터가 1일 떄

itr = lst.erase(itr); // 무조건 삭제하고 다음으로 진행한다

else

++itr; // itr를 다음으로 넘긴다.

}

*/

//==============================================

//==============================================

// 연관 배열 클래스 std::map

/*

1. 연관 배열 (Associative Array) 클래스

- 검색이 가능한 키(Key)와 그 키에 대응하는 밸류(value)가 페어(pair)로

구성된 컨테이너 클래스.

- 보유한 데이터에 키를 지정하여 값을 고속으로 탐색, 출력할 수 있다.

2. 특징

- 단순한 배열을 사용하여 값을 취득할 경우 처리시간 O(N)인데

map은 O(log N)이다. / N보다는 한 단계 고속 / 이는 map이

이진 트리(binary tree)로써 구성되어 있기 때문이다.

* O(log N) : 입력 값 N이 주어지고 연산을 실행할 때 필요한 단계가 특정

요인에 의해 줄어듬

* O(N)이면 요소 수가 1천배일 때 처리시간도 1천배가 된다. 반면에 이

진트리의 O(log N)이면 요소 수가 1천배로 증가해도 처리시간의 증가는

약 10배에 지나지 않음.

- map과 비슷한 클래스가 C++11 에서 unordered_map 이라는 이름으로 존재.

이는 해쉬를 사용하기 때문에 키에서 데이터를 추출하는 속도가 O(1)이라 매우 빠름

- 하지만 이름에서 알 수 있듯이 요소가 순서대로 정렬되어 있지 않아 정렬하고 싶은

경우에는 map을 사용하게 된다.

- 또한, 하나의 키에 여러개의 값을 대응시킬 수 있는 multimap도 있다.

*/

// std::map의 선언

/*

1. 개요

- vector와 list 둘과 map의 가장 큰 차이점은 삽입하는 요소의 형태가 2종류라는 것이다.

- 첫번째 인수는 키 (key)라고 불리며, string이나 int 같은 타입들을 지정할 수 있다.

단, 아무 타입이나 지정할 수 있는 것은 아니며, 노드를 키 순서대로 정렬하기 위해

키 객체의 순서가 계산 가능한 것이 아니면 안된다.

[문법]

#include <map>

...

std::map<key, value> 객체명;

std::map<string, int> map_; //string &int의 연관 배열 map_ 생성

*/

// std::map의 값 설정과 참조

/*

1. key에 대응하는 value를 설정

- 연관 배열에서 key에 대응하는 값을 설정하려면 객체명[key] = value로

해야한다.

[예시]

std::map<std::string, int> mp;

mp["abc"] = 123;

- key-value설정이 완료되면 map 내부의 트리에 저장된다.

- 한 번 설정한 key에는 값을 여러 번 바꿀 수 있다. 덮어쓰기가 가능함

[예시]

mp["abc"] = 123;

mp["abc"] = 456;

mp["abc"] = 789;

2. key에 대응하는 value를 취득하는 방법

- 객체명[key]를 입력하는 것만으로 map에서 value를 불러올 수 있다.

- 그러나, 만약에 value가 설정되어 있지 않은 key를 불러오면 value의 데이터

타입에 기반한 디폴트 값을 불러온다.

[예시]

std::map<std::string, int> mp;

mp["Lotus"] = 123;

cout << mp["Lotus"] << endl; // mp["Lotus"] 의 값 123을 불러옴

cout << mp["IBM"] << endl; // mp["IBM"]은 없으므로 0을 표시

- 사실 [] 연산자가 반환하는 타입은 (const가 아닌) 값의 참조다. 따라서 연관 배열

이 보유하고 있지 않은 키를 사용하면 그 키와 디폴트 값으로 구성된 노드가 작성되

므로 주의할 것.

[예시]

std::map<std::string, int> mp;

mp["Lotus"] = 123;

const std::map<std::string ,int> mp2(mp); // 상수 mp2 을 생성

cout << mp2["Lotus"] << "\n"; // 탐색할 수 없음

- 위 예시 뿐만 아니라 인수로써 const map이 들어가는 경우도 있다. 이때도 []연산자는 사용불가능.

- 연관 컨테이너 const일 경우에 값을 참조하고 싶으면 at() 멤버 함수를 사용한다. 이 함수는 const도

정의되어 있으므로 안전하다.

[예시]

std::map<std::string, int> mp;

mp["Lotus"] = 123;

const std::map<std::string, int> mp2(mp); // mp를 기반으로 mp2 생성

cout << mp2.at("Lotus") << "\n"; // 123이 표시됨

*/

// 모든 key, value의 데이터 취득(iterator)

/*

- map 에서는 객체명.begin()과 객체명.end()로 데이터를 취득할 수 있다.

- 이때 이 데이터는 페어(std::pair)로 구성되어 있어 키와 값을 가각 itr->first, itr->second

로 받는다.

[예시]

std::map<std::string, int> mp;

for(auto itr = mp.begin(); itr !=mp.end(); ++itr)

{

std::cout << "key = " << itr->first << ", val = " << itr->second << "\n";

}

*/

// std::map에서 key를 검색

/*

1.find() : iterator

- 객체명.find(key)를 사용하여 key에 map이 존재하는지 검사한다.

- key가 설정되어 있을 경우, key와 value pair의 iterator를 반환.

- key가 설정되어 있지 않을 경우, end()를 반환.

[예시]

map<string, int> mp;

auto itr = mp.find("xyz");

if(itr != mp.end())

{

// 값이 있을 때의 처리

}

else

{

// 값이 없을 때의 처리

}

2. lower_bound(key) & upper_bound(key)

- 이진 탐색 기반의 검색용 함수

1) lower_bound(key)

- 자신을 포함하여 인수로 지정된 key보다 큰 최초의 key에

대한 iterator를 반환한다.

2) upper_bound(key)

- 인수로 지정된 key보다 큰 최초의 key에 대한 iterator를 반환한다.

*/

//==============================================

//==============================================

728x90

'C++' 카테고리의 다른 글

C++ 조건부 컴파일  (0) 2021.08.16
C++ 전처리 지시자  (0) 2021.08.16
C++ PlaySound  (0) 2021.08.14
C++ 클래스와 정보은닉  (0) 2021.08.12
C++ 객체지향, 동적할당  (0) 2021.08.12