이 글에서는 C++에서 사용자 정의 클래스(user-defined class)를 키(key)로 사용하는 unordered_map을 생성하는 방법을 알아봅니다.
unordered_map은 내부적으로 해시 테이블을 기반으로 동작하기 때문에, 기본 자료형(int, string 등)이 아닌 사용자 정의 클래스를 키로 사용하려면 다음 두 가지 요소가 반드시 필요합니다.
- 동등성 비교 연산자(operator==): 해시 충돌이 발생했을 때 두 키가 실제로 같은지 비교하기 위해 필요합니다.
- 사용자 정의 해시 함수: 클래스 객체를 size_t 타입의 해시 값으로 변환하는 함수로, 템플릿의 세 번째 인자로 전달합니다.
예제 코드
#include <bits/stdc++.h>
using namespace std;
// 키 값으로 사용할 클래스 객체
struct Person {
string first, last;
Person(string f, string l){
first = f;
last = l;
}
bool operator==(const Person& p) const{
return first == p.first && last == p.last;
}
};
class MyHashFunction {
public:
// 이름 길이의 합을 해시 값으로 사용
size_t operator()(const Person& p) const{
return p.first.length() + p.last.length();
}
};
int main(){
unordered_map<Person, int, MyHashFunction> um;
Person p1("kartik", "kapoor");
Person p2("Ram", "Singh");
Person p3("Laxman", "Prasad");
um[p1] = 100;
um[p2] = 200;
um[p3] = 100;
for (auto e : um) {
cout << "[" << e.first.first << ", "<< e.first.last<< "] => " << e.second << '\n';
}
return 0;
}실행 결과
[Laxman, Prasad] => 100 [kartik, kapoor] => 100 [Ram, Singh] => 200
코드 설명
Person 구조체는 이름(first)과 성(last)을 멤버 변수로 가지며, operator==를 오버로딩하여 두 객체가 같은 사람인지 비교할 수 있도록 합니다. 이 연산자가 없으면 해시 충돌 시 키의 동등성을 판단할 수 없어 컴파일 오류가 발생합니다.
MyHashFunction 클래스는 operator()를 정의하여 Person 객체의 이름과 성 길이의 합을 해시 값으로 반환합니다. 이렇게 만든 해시 함수 클래스를 unordered_map<Person, int, MyHashFunction>처럼 세 번째 템플릿 인자로 지정하면 됩니다.
다만 이름 길이의 합처럼 단순한 해시 함수는 서로 다른 객체가 동일한 해시 값을 가지는 해시 충돌이 자주 발생할 수 있습니다. 실무에서는 문자열의 각 문자를 활용하거나 표준 해시 알고리즘을 조합하는 등 충돌 가능성을 낮추는 해시 함수를 설계하는 것이 좋습니다.