map은 키-값 쌍을 저장하고 검색하는 데 사용되는 자료구조 중 하나로 사용법은 아래와 같다.
1. map 헤더파일 포함
#include <map>
2. map 객체 생성하기
생성시 키와 값의 자료형을 지정해야하는데 개인적으로는 문자열 키와 함수포인터 값을 가지는 map을 자주사용한다.
map<string, int> myMap;
3. 원소 추가하기 (insert)
원소를 추가할 때는 키-값 쌍을 pair 객체로 전달하여 추가하는데 insert 함수를 사용하는 방법도 있고 아래의 4번을 사용하는 방법도 있다. 각 키는 고유해야 하며, 각 키에 대응하는 값이 하나씩 존재하게 된다.
pair 객체를 만들때는 pair<type1, type2>로 생성하거나 make_pair 함수를 사용해도 되고 혹은 {} 중괄호를 사용하는 방법도 있다.
또한 map는 중복을 허용하지 않는 자료구조로 insert로 중복키 삽입을 시도하면 추가되지 않는다.
또한 insert 함수는 삽입 작업이 성공했는지 실패했는지를 나타내는 pair<iterator, bool>를 반환하는데 여기서 bool 값을 확인해 true이면 새로운 요소가 삽입됐음을, false이면 중복된 키 때문에 삽입이 무시됐음을 알 수 있다.
#include <iostream>
#include <map>
using namespace std;
int main() {
map<string, int>myMap;
myMap.insert(pair<string, int>("apple", 5));
myMap.insert(make_pair("banana", 3));
myMap.insert({"orange", 4});
auto result = myMap.insert({"apple", 10}); // 중복 삽입 시도 -> 추가 되지 않음
if (!result.second) {
cout << "오류: 키가 이미 존재합니다!" << endl;
} else {
cout << "키 삽입 성공!" << endl;
}
for(auto &pair : myMap) {
cout << "Key: " << pair.first << ", Value: " << pair.second << endl;
}
return 0;
}
✔ emplace
emplace 메소드는 emplace_back과 유사한 개념이지만, emplace_back이 주로 std::vector, std::deque, std::list 같은 시퀀스 컨테이너의 끝에 요소를 추가할 때 사용된다면, emplace는 키-값 쌍을 갖는 std::map이나 요소의 유니크한 값을 갖는 std::set과 같은 연관 컨테이너에 요소를 추가할 때 사용된다. insert와 emplace의 관계는 마치 push_back과 emplace_back과 같은데 insert는 컨테이너에 요소를 추가할 때 해당 요소의 복사본을 생성하거나, C++11 이후에는 이동을 사용해 요소를 추가하는 반면, emplace는 전달된 생성에 필요한 인자를 이용해 컨테이너 내에서 객체를 직접 "in-place"로 생성하므로 특히 복사나 이동 생성자의 비용이 큰 객체를 컨테이너에 추가할 때 효율적이다.
4. [키]로 값에 접근하여 원소 추가 또는 수정하기
map[키] = 값 형태를 사용하면 해당 키가 이미 map에 존재하는 경우 그 키의 값을 업데이트하고, 키가 존재하지 않는 경우 새로운 키-값 쌍을 map에 추가할 수 있다.
myMap["apple"] = 5; // apple 키에 5 할당
myMap["banana"] = 3; // banana 키에 3 할당
myMap["apple"] = 10; // apple 키의 값을 10으로 수정
int-string 페어라면 myMap[10] = "apple" 로 추가 또는 수정한다.
5. 원소 삭제하기
erase(키) 함수 사용
myMap.erase("banana"); // banana 원소 삭제
6. 반복문 통한 전체 map 탐색
map에서 키-값 쌍을 가져올 때는 key는 first로 값은 second로 접근할 수 있다.
for (const auto& pair : myMap) {
cout << pair.first << " = " << pair.second << endl;
}
7. map에서 원소 검색하기
auto it = myMap.find("apple");
if (it != myMap.end()) {
int value = it->second;
cout << "apple의 값: " << value << endl;
} else {
cout << "apple을 찾을 수 없습니다." << endl;
}
8. map의 정렬 순서
map은 자동으로 정렬을 유지하는 컨테이너로, 키를 기준으로 오름차순 정렬을 유지한다.
정렬 기준은 키의 타입에 정의된 비교 연산자(operator <)에 의해 결정되는데 만약 키가 문자열이라면 알파벳 순으로, 정수라면 숫자의 크기 순으로 정렬되는 식이다.
때문에 요소의 삽입, 제거가 빈번할 경우 성능이 저하될 수 있으며 정렬할 필요가 없는 경우 map 대신 unordered_map을 사용할 수 있다.
#include <iostream>
#include <map>
using namespace std;
int main() {
map<int, string> myMap;
// map에 값들을 "무작위" 순서로 삽입
myMap[5] = "five";
myMap[2] = "two";
myMap[3] = "three";
myMap[1] = "one";
myMap[4] = "four";
// map을 순회하며 키와 값을 출력
for(const auto& pair : myMap) {
cout << pair.first << ": " << pair.second << endl;
}
return 0;
}
9. map 값의 초기화
맵에 새로운 키로 접근하면 해당 키-값 쌍이 자동으로 생성되고, 값은 해당 타입의 기본 생성자를 사용하여 초기화된다.
따라서 추가적인 초기화 로직 없이 아래 예시와 같이 값을 업데이트해도 올바르게 동작할 것으로 기대할 수 있다. 이는 값의 데이터 타입이 컨테이너인 경우에도 마찬가지이다.
#include <iostream>
#include <map>
using namespace std;
int main() {
map<string, int> myMap;
// 키가 존재하지 않는 경우, 자동으로 해당 키를 가진 새로운 요소를 생성하고 기본 생성된 값을 반환
myMap["1번"]++;
myMap["2번"] += 2;
for(const auto& pair : myMap) {
cout << pair.first << ": " << pair.second << endl;
}
return 0;
}
10. 다양한 map의 사용예시
값이 vector인 예시.
#include <iostream>
#include <map>
using namespace std;
int main() {
map<string, vector<int>> scores;
// 키가 맵에 존재하지 않으면, 그 키에 대해 비어 있는 vector<int>가 자동으로 생성되고, 바로 push_back 메소드를 사용해 해당 벡터에 요소를 추가할 수 있다.
scores["John"].push_back(85); // "John"의 점수 목록에 85 추가
scores["Jane"].push_back(90); // "Jane"의 점수 목록에 90 추가
// "John"의 모든 점수 출력
for (int &score : scores["John"]) {
cout << "John's score: " << score << endl;
}
return 0;
}
값이 map인 예시.
#include <iostream>
#include <map>
using namespace std;
int main() {
map<string, map<string, int>> storeInventory;
// 키가 맵에 존재하지 않으면, 그 키에 대해 비어 있는 map<string,int>가 자동으로 생성되고, 바로 [] 연산자를 사용해 해당 맵에 요소를 추가할 수 있다.
storeInventory["fruit"]["apple"] = 30; // 과일 섹션의 사과 수량 설정
storeInventory["vegetable"]["carrot"] = 20; // 채소 섹션의 당근 수량 설정
// 과일 섹션의 사과 수량 출력
cout << "Apples in fruit section: " << storeInventory["fruit"]["apple"] << endl;
return 0;
}
값이 함수포인터인 예시.
#include <iostream>
#include <map>
#include <functional>
void Function1() {
cout << "Function1이 호출되었습니다." << endl;
}
void Function2() {
cout << "Function2가 호출되었습니다." << endl;
}
int main() {
map<string, function<void()>> functionMap;
functionMap["Function1"] = Function1;
functionMap["Function2"] = Function2;
string key = "Function1";
auto it = functionMap.find(key);
if (it != functionMap.end()) {
functionMap[key](); // 또는 it->second();
} else {
cout << "키를 찾을 수 없습니다." << endl;
}
return 0;
}
값이 pair인 예시.
map이 아닌 pair는 내부적으로 정렬 순서를 유지하지 않는다.
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
void print1(const string &msg)
{
cout << "print1 called. msg is " << msg << endl;
}
void print2(const string &msg)
{
cout << "print2 called. msg is " << msg << endl;
}
int main()
{
map<string, pair<int, void (*)(const string&)>> myDoubleMap;
myDoubleMap["green"] = make_pair(5, print1);
myDoubleMap["red"] = make_pair(3, print2);
string toFind("red");
auto it = myDoubleMap.find(toFind);
if(it != myDoubleMap.end())
{
if(it->second.first = toFind.length())
{
it->second.second(toFind);
}
}
return 0;
}
키가 class인 예시.
앞에서 설명한 것처럼 map은 내부적으로 < 연산자를 통해 키에 대해 오름차순 정렬된 상태를 유지하므로, 사용자 정의 타입 키를 사용하는 경우 operator<를 재정의 해줘야 한다.
#include <iostream>
#include <map>
#include <string>
class Person {
public:
std::string name;
int age;
Person(const std::string& name, int age) : name(name), age(age) {}
// operator<를 재정의하지 않으면 오류가 남, 재정의를 통해 멤버변수인 이름을 기준으로 정렬.
bool operator<(const Person& other) const {
return name < other.name;
}
};
int main() {
std::map<Person, int> people;
people.emplace(Person("Alice", 30), 1);
people.emplace(Person("Bob", 25), 2);
people.emplace(Person("Charlie", 35), 3);
// 출력: Alice, Bob, Charlie
for (const auto& [person, value] : people) {
std::cout << person.name << std::endl;
}
return 0;
}
'프로그래밍 > C++' 카테고리의 다른 글
C++ ] std::thread 스레드 사용법 (0) | 2023.09.25 |
---|---|
C++ ] 가변인자 받아서 string 또는 CString으로 출력하기 (0) | 2023.09.24 |
C++ ] 레지스트리 등록하여 윈도우 시작시 앱 자동 실행 하기 (0) | 2023.09.22 |
Windows 프로그래밍 시 OutputDebugString 사용해 디버깅 정보 출력하기 + 가변인자 받아서 디버깅 정보 출력 (0) | 2023.09.21 |
C++ ] 현재 실행중인 실행파일의 경로 얻기, GetModuleFileName (0) | 2023.09.19 |