STL
- 메모리 자동 관리가 제공되는 자료구조 라이브러리.
- 템플릿 프로그래밍 기반이라, 다양한 자료형의 데이터들을 저장.
- STL 자료구조에는 벡터(Vector) / 맵(Map) / 셋(Set) / 스택(Statck) / 큐(Queue) / 리스트(List) 등이 있음.
- 벡터와 맵 정도만 쓰이고, 셋이 아주 가끔 쓰이는 편.
- 다른 말로는 컨테이너라고도 함.
STL 대체품
- 언리얼 엔진 객체들의 전용 컨테이너
- TArray, TMap, TMultiMap, Tset
- EA의 EASTL
- STL과 호환
- 메모리 등을 고친 컨테이너
벡터(Vector)
- 데이터 개수가 증가함에 따라 자동으로 메모리를 관리해주는 배열
- 동적 배열
- 배열이기 때문에 그 안에 저장된 모든 요소들은 연속된 메모리 공간에 위치
- 어떤 요소에도 임의로 접근(Random Access)가 가능
push_back()
벡터의 맨 뒤에 요소를 추가
Scores.push_back(30);
Names.push_back("Navi");
MyCats.push_back(NewCat);
pop_back()
벡터의 맨 뒤에 있는 요소를 제거
비어있는 벡터에 pop_back() 함수 호출하면 런타임 오류가 발생할 수 있음.
Scores.pop_back();
capacity()
저장 가능한 최대 요소 개수를 반환
size()
현재 저장된 요소 개수를 반환
reserve(size_t Newcapacity)
벡터의 현재 capacity를 수정.
_Newcapacity가 기존 capacity보다 작은 경우: 아무일도 발생하지 않음.
_Newcapacity가 기존 capacity보다 큰 경우: 메모리 재할당 발생.
값 초기화는 해주지 않음.
Scores.reserve(10u); // 10u는 unsigned int
벡터의 size가 커지다가 capacity를 넘어서게 되면 더 넓은 메모리를 동적 할당하고, 기존 데이터를 모두 복사한다.
불필요한 재할당 및 복사를 막으려면 처음부터 capacity를 크게 잡아야한다.
이때 사용하는 함수가 reserve()이다.
// Main.cpp
#include <iostream>
#include <vector>
int main()
{
std::vector<int> Scores;
Scores.reserve(8u);
Scores.push_back(100);
Scores.push_back(98);
std::cout << "Current capacity: " << Scores.capacity() << std::endl;
std::cout << "Current size: " << Scores.size() << std::endl;
for (size_t i = 0; i < Scores.size(); ++i)
{
std::cout << "Scores[" << i << "] : " << Scores[i] << std::endl;
}
Scores.pop_back();
std::cout << "Current capacity: " << Scores.capacity() << std::endl;
std::cout << "Current size: " << Scores.size() << std::endl;
for (size_t i = 0; i < Scores.size(); ++i)
{
std::cout << "Scores[" << i << "] : " << Scores[i] << std::endl;
}
// delete[] Scores; 구문이 필요없습니다.
// Scores는 STL Vector이므로, 내부적으로 메모리 해제도 해줍니다.
return 0;
}
vector 자체는 동적할당이지만, 내부적으로 메모리 해제를 해주므로 delete[] v 구문은 필요없다.
. u 접미사의 의미
- 4: 기본적으로 int (signed int, 부호가 있는 정수) 타입.
- 4u: unsigned int 타입입. 값은 똑같이 4지만, 메모리에서 데이터를 해석하는 방식이 "부호가 없음"으로 고정.
왜 reserve에서 4u를 쓰는가? (타입 일치)
std::vector::reserve(size_type n) 함수는 인자로 size_type을 받는다. C++ 표준 라이브러리에서 이 타입은 보통 unsigned int 또는 size_t와 같은 부호 없는 정수형. 아주 엄격한 컴파일러 설정에서는 "signed/unsigned mismatch" 경고를 낼 수 있는데, 4u라고 써주면 이런 경고를 원천 차단
resize(const size_t _Newsize)
벡터의 현재 size를 수정한다. 새로 생긴 데이터는 0에 준하는 값으로 초기화된다.
- _Newsize가 기존 size보다 작으면 초과분을 삭제한다.
- _Newsize가 size보다 크고, capacity보다는 작으면 정상 추가된다.
- _Newsize가 기존 capacity보다 크면 메모리 재할당이 발생한다.
벡터는
clear()
벡터의 내용을 전부 지운다.
size는 0이 되고 capacity는 변하지 않는다.
Scores.clear();
assign(size_t n, T x);
n개의 값을 벡터에 대입한다.
std::vector<int> D1Vector;
D1Vector.assign(16u, 100); // 16 size의 공간
std::vector<std::vector<float>> D2Vector;
D2Vector.assign(4u, vector<float>(8u, 0.f); // 4 x 8 2차원 배열을 모두 0.f로 초기화
vector와 객체
기본 자료형 외에 사용자 정의 자료형(클래스)의 객체를 저장하는 벡터

// Main.cpp
#include <iostream>
#include <vector>
class Score
{
public:
Score(int InID, float InValue)
: ID(InID)
, Value(InValue)
{
}
public:
int ID;
float Value;
};
void PrintScores(const std::vector<Score>& InScores);
int main()
{
std::vector<Score> Scores;
Scores.reserve(2);
Scores.push_back(Score(100, 95.f));
Scores.push_back(Score(110, 70.f));
PrintScores(Scores);
std::vector<Score> ClonedScores = Scores;
ClonedScores[0].Value = 100.f;
PrintScores(ClonedScores);
return 0;
}
void PrintScores(const std::vector<Score>& InScores)
{
std::cout << "Current capacity: " << InScores.capacity() << std::endl;
std::cout << "Current size: " << InScores.size() << std::endl;
for (size_t i = 0; i < InScores.size(); ++i)
{
std::cout << "InScores[" << i << "] : " << InScores[i].ID << ", " << InScores[i].Value << std::endl;
}
}
객체를 보관하는 벡터의 동작
std::vector<Score> Scores;
Scores.reserve(2);
Scores.push_back(Score(100, 95.f));
Scores.push_back(Score(110, 70.f));
Scores.push_back(Score(200, 98.f));
// 위 코드를 실행 할 때 capacity가 부족하고, 재할당과 복사가 일어납니다.

객체의 포인터를 보관하는 벡터

// Main.cpp
#include <iostream>
#include <vector>
class Score
{
public:
Score(int InID, float InValue)
: ID(InID)
, Value(InValue)
{
}
public:
int ID;
float Value;
};
void PrintScores(const std::vector<Score*>& InScores);
int main()
{
std::vector<Score*> Scores;
Scores.reserve(2);
Scores.push_back(new Score(100, 95.f));
Scores.push_back(new Score(110, 70.f));
PrintScores(Scores);
std::vector<Score*> ClonedScores = Scores;
ClonedScores[0]->Value = 100.f;
PrintScores(Scores);
PrintScores(ClonedScores);
size_t Size = Scores.size();
for (size_t i = 0; i < Size; ++i)
{
if (Scores[i] != nullptr)
{
delete Scores[i]; // Scores 벡터는 메모리 주소값만 관리할 뿐, 실제 객체는 사용자가 delete 해야함.
Scores[i] = nullptr;
}
}
ClonedScores.clear();
Scores.clear();
return 0;
}
void PrintScores(const std::vector<Score*>& InScores)
{
std::cout << "Current capacity: " << InScores.capacity() << std::endl;
std::cout << "Current size: " << InScores.size() << std::endl;
for (size_t i = 0; i < InScores.size(); ++i)
{
std::cout << "InScores[" << i << "] : " << InScores[i]->ID << ", " << InScores[i]->Value << std::endl;
}
}
객체의 포인터를 보관하는 벡터의 동작
std::vector<Score*> Scores;
Scores.reserve(2);
Scores.push_back(new Score(100, 95.f));
Scores.push_back(new Score(110, 70.f));
Scores.push_back(new Score(200, 98.f));
// 마지막 줄을 실행 할 때 capacity가 부족하고, 재할당 및 복사가 발생합니다.
size_t Size = Scores.size();
for (size_t i = 0; i < Size; ++i)
{
if (Scores[i] != nullptr)
{
delete Scores[i];
Scores[i] = nullptr;
}
}
Scores.clear();

벡터의 장점
인덱스를 통해 O(1) 시간 복잡도로 임의 접근이 가능하다.
벡터의 단점
중간에 요소 삽입 및 삭제는 느리다. 요소를 한 칸씩 밀거나 당겨야하기 때문이다.
- 맨 앞에 요소를 추가하거나 중간 요소를 삭제한다면 복사 문제 발생
- 원래 있던 요소들을 한칸씩 미뤄서 복사를 해줘야한다.
- 만약 가득 찬 vector에 insert가 이루어진다고 하면 새로운 메모리에 재할당한 뒤에 원래 있던 데이터를 복사하고, 첫번째에 값을 insert() 하게 되어 비용이 많이 든다.
iterator
operator[](size_t InIndex);
지정된 위치(InIndex)의 요소를 참조로 반환
Scores[3] = 7;
std::cout << Names[3] << " ";
std::cout << MyCats[2].GetAge() << " ";
Vector와 [] 연산자
// Main.cpp
#include <iostream>
#include <vector>
int main()
{
std::vector<int> Scores;
Scores.reserve(8);
Scores.push_back(100);
Scores.push_back(98);
Scores.pop_back();
if (Scores.empty() == true)
{
return 0;
}
int ScoreCount = static_cast<int>(Scores.size() - 1u);
for (int i = ScoreCount; i >= 0; --i)
{
std::cout << Scores[i] << std::endl;
}
return 0;
}
[] 연산자는 특정 컨테이너만 쓸 수 있다. std:list, std::set은 operator[]가 없다.
단, 반복자(iterator)를 이용하면 모든 STL 컨테이너를 순회할 수 있다.
반복자(iterator)
Iterator는 컨테이너 내부 요소를 가리키며 이동하는 객체라고 할 수 있다. 포인터처럼 동작하는 순회 도구이다
for (auto it = v.begin(); it != v.end(); ++it) {
cout << *it << endl;
}
- it → 현재 요소를 가리킴
- *it → 값 접근
- ++it → 다음 요소로 이동
👉 포인터처럼 *, ++ 사용
index 방식은 배열과 벡터에서만 사용할 수 있고, iterator는 모든 컨테이너에서 사용할 수 있다.
map에서 m["fruit"] 같은 형식을 쓸 수는 있지만, 이는 fruit이라는 키를 가진 값을 탐색한다는 의미로 저장 위치인 index와는 다르다.
- begin() : 자료구조의 첫 번째 요소를 가리키는 반복자 반환
- end(): 자료구조의 마지막 요소 바로 뒤의 요소를 가리키는 반복자 반환
- 마지막 요소 바로 뒤 요소를 가리키는 이유
- 비어있는 컨테이너에서는 마지막 요소가 없다. 마지막 요소를 가리킬 수가 없는 경우에는 마지막 요소 바로 뒤를 가리킨다는 개념을 적용해서 첫 부분을 가리킬 수 있게 된다. begin() == end() 조건식으로 비어있는 컨테이너를 표현할 수 있다.
- 컨테이너에서 원하는 데이터를 찾지 못한 경우에 end iterator를 반환하면 의미가 적절하다.
- 마지막 요소 바로 뒤 요소를 가리키는 이유
// vector<자료형>::iterator <변수명>
std::vector<int>::iterator BeginIter = Scores.begin();
std::vector<int>::iterator EndIter = Scores.end();

역방향 반복자
- rbegin(): vector의 마지막 요소를 가리키는 역방향 반복자를 반환
- rend(): vector의 첫 번째 요소 앞을 가리키는 역방향 반복자를 반환
// Main.cpp
#include <iostream>
#include <vector>
void PrintScores(const std::vector<int>& InScores);
void PrintScoresInReverse(const std::vector<int>& InScores);
int main()
{
std::vector<int> Scores;
Scores.reserve(8);
Scores.push_back(100);
Scores.push_back(98);
Scores.push_back(70);
Scores.push_back(86);
Scores.push_back(65);
PrintScores(Scores);
Scores.pop_back();
Scores.pop_back();
PrintScoresInReverse(Scores);
Scores.reserve(7);
PrintScoresInReverse(Scores);
return 0;
}
void PrintScores(const std::vector<int>& InScores)
{
// InScores가 const이므로 const_iterator를 사용해야 한다.
std::vector<int>::const_iterator LeftIter = InScores.begin();
std::vector<int>::const_iterator RightIter = InScores.end();
while (LeftIter != RightIter)
{
std::cout << *LeftIter << std::endl;
++LeftIter;
}
std::cout << "Current capacity: " << InScores.capacity() << std::endl;
std::cout << "Current size: " << InScores.size() << std::endl;
std::cout << std::endl;
}
void PrintScoresInReverse(const std::vector<int>& InScores)
{
// InScores가 const이므로 const_reverse_iterator를 사용해야 한다.
std::vector<int>::const_reverse_iterator RightIter = InScores.rbegin();
std::vector<int>::const_reverse_iterator LeftIter = InScores.rend();
while (RightIter != LeftIter)
{
std::cout << *RightIter << std::endl;
++RightIter;
}
std::cout << "Current capacity: " << InScores.capacity() << std::endl;
std::cout << "Current size: " << InScores.size() << std::endl;
std::cout << std::endl;
}
포인터 객체 벡터에서의 delete
// Main.cpp
#include <iostream>
#include <vector>
class Score
{
public:
Score(int InID, float InValue)
: ID(InID)
, Value(InValue)
{
}
public:
int ID;
float Value;
};
int main()
{
std::vector<Score*> Scores;
Scores.reserve(4u);
Scores.push_back(new Score(100, 95.f));
Scores.push_back(new Score(110, 70.f));
Scores.push_back(new Score(200, 98.f));
std::cout << "Scores's size: " << Scores.size() << std::endl;
std::cout << "Scores's capacity: " << Scores.capacity() << std::endl;
{
std::vector<Score*>::iterator LeftIter = Scores.begin();
while (LeftIter != Scores.end())
{
if (110 == (*LeftIter)->ID)
{
delete *LeftIter;
LeftIter = Scores.erase(LeftIter);
}
else
{
++LeftIter;
}
}
}
std::cout << "Scores's size: " << Scores.size() << std::endl;
std::cout << "Scores's capacity: " << Scores.capacity() << std::endl;
{
std::vector<Score*>::iterator LeftIter = Scores.begin();
std::vector<Score*>::iterator RightIter = Scores.end();
while (LeftIter != RightIter)
{
delete *LeftIter;
++LeftIter;
}
Scores.clear();
}
std::cout << "Scores's size: " << Scores.size() << std::endl;
std::cout << "Scores's capacity: " << Scores.capacity() << std::endl;
return 0;
}
Pair
두 데이터를 한 단위로 저장하는 구조
// std::pair<key 자료형, value 자료형>
std::pair<int, float> Score01(100, 90.f);
std::pair<int, float> Score02(110, 70.f);
Map
Key와 Value의 쌍들을 저장. 키와 값이 매핑되어 있어 Map이라고 부른다.
이진탐색트리 기반이고, 키는 중복될 수 없다.
// std::map<key 자료형, value 자료형> 변수명;
std::map<int, float> ScoreMap;
중복값을 허용하는 맵으로 MultiMap이 있다.
#include <iostream>
#include <map>
using namespace std;
int main() {
multimap<string, int> mm;
mm.insert({"apple", 1});
mm.insert({"banana", 2});
mm.insert({"apple", 3}); // 같은 키 또 추가
for (auto& p : mm) {
cout << p.first << " : " << p.second << endl;
}
}
auto range = mm.equal_range("apple");
for (auto it = range.first; it != range.second; ++it) {
cout << it->second << endl;
}
// 결과
// 1
// 2
equal_range = 해당 키의 전체 범위 반환
std::pair<iterator, bool> insert 함수
새 요소를 맵에 삽입하고, 반복자와 bool 값을 한 쌍으로 반환한다.
반복자는 요소를 가리키고 bool 값은 삽입 결과를 알려준다.
ScoreMap.insert(std::pair<int, float>(100, 95.f));
// (iterator, true)를 반환합니다.
ScoreMap.insert(std::pair<int, float>(100, 70.f));
// Map 컨테이너에 key가 중복으로 삽입되었으므로, (iterator, false)를 반환합니다.
operator[]() 연산자
key에 대응하는 값을 참조로 반환
맵에 키가 없으면 새 요소 삽입, 이미 있으면 덮어쓰기
std::map<int, float> ScoreMap;
ScoreMap[100] = 95.f; // 새 요소를 삽입합니다.
ScoreMap[100] = 0.f; // 100의 값을 덮어씁니다.
float ReturnValue(std::map<int, float>& InScoreMap, const int InKey)
{
return InScoreMap[InKey];
// 만약 InKey에 해당하는 키-벨류 쌍이 InScoreMap에 없다면, 그 순간 (InKey, 0.f) 페어를 삽입해버리고 0.f가 반환됩니다.
// Map 컨테이너의 배열 첨자 연산자를 사용할 땐 용법을 정확히 이해하고 사용하는 것이 중요합니다.
}
// Main.cpp
#include <iostream>
#include <map>
void PrintScores(const std::map<int, float>& InScoreMap);
int main()
{
std::map<int, float> ScoreMap;
ScoreMap.insert(std::pair<int, float>(100, 95.f));
ScoreMap.insert(std::pair<int, float>(110, 70.f));
PrintScores(ScoreMap);
ScoreMap[110] = 90.f;
PrintScores(ScoreMap);
ScoreMap.insert(std::pair<int, float>(100, 90.f));
// operator[]와 다르게 중복 키가 삽입되어도 값 덮어쓰기가 발생하지 않습니다.
std::cout << "ScoreMap[100] Score: " << ScoreMap[100] << std::endl;
std::cout << "ScoreMap[115] Score: " << ScoreMap[115] << std::endl;
// 이렇게 읽으면 위험할 수 있습니다. 읽기만 하려 했는데,
// 해당 키가 없다면 키-값 쌍 생성 및 값을 0에 준하는 값으로 초기화 해버립니다.
// 즉, ReadOrCreate
return 0;
}
void PrintScores(const std::map<int, float>& InScoreMap)
{
std::map<int, float>::const_iterator Iter = InScoreMap.begin();
while (Iter != InScoreMap.end())
{
std::cout << "ID: " << Iter->first << " Value: " << Iter->second << std::endl;
++Iter;
}
std::cout << "Size: " << InScoreMap.size() << std::endl << std::endl;
}
C++ 맵은 key 기준으로 자동정렬된다.
// Main.cpp
#include <iostream>
#include <map>
void PrintScores(const std::map<int, float>& InScoreMap);
int main()
{
std::map<int, float> ScoreMap;
ScoreMap.insert(std::pair<int, float>(110, 70.f));
ScoreMap.insert(std::pair<int, float>(200, 98.f));
ScoreMap.insert(std::pair<int, float>(100, 95.f));
PrintScores(ScoreMap);
return 0;
}
void PrintScores(const std::map<int, float>& InScoreMap)
{
std::map<int, float>::const_iterator Iter = InScoreMap.begin();
while (Iter != InScoreMap.end())
{
std::cout << "ID: " << Iter->first << " Value: " << Iter->second << std::endl;
++Iter;
}
std::cout << "Size: " << InScoreMap.size() << std::endl << std::endl;
}
map에서 [] 연산자로 요소 찾기
요소가 없는 경우에는 삽입을 하니, 요소 찾기는 불가능하다.
- iterator find() 함수
- map에서 key를 찾으면 그에 대응하는 값을 가리키는 반복자를 반환
- 못 찾으면 end()를 반환
- erese() 함수
- map의 요소를
std::map<std::string, int>::iterator FoundIter = ScoreMap.find("Park");
ScoreMap.erase(FoundIter);
함수 예시
// Main.cpp
#include <iostream>
#include <map>
void PrintScores(const std::map<int, float>& InScoreMap);
int main()
{
std::map<int, float> ScoreMap;
ScoreMap.insert(std::pair<int, float>(100, 95.f));
ScoreMap.insert(std::pair<int, float>(110, 70.f));
ScoreMap.insert(std::pair<int, float>(200, 98.f));
PrintScores(ScoreMap);
std::map<int, float>::iterator Iter = ScoreMap.find(110);
if (Iter != ScoreMap.end())
{
ScoreMap.erase(Iter);
}
PrintScores(ScoreMap);
return 0;
}
void PrintScores(const std::map<int, float>& InScoreMap)
{
std::map<int, float>::const_iterator Iter = InScoreMap.begin();
while (Iter != InScoreMap.end())
{
std::cout << "ID: " << Iter->first << " Value: " << Iter->second << std::endl;
++Iter;
}
std::cout << "Size: " << InScoreMap.size() << std::endl << std::endl;
}
사용자 정의 클래스를 키로 사용하는 맵
맵 컨테이너는 이진 탐색 트리를 기반으로 만들어져, 삽입/삭제 시 자동 정렬된다.
사용자 정의 클래스를 키로 사용하려면 정렬 관련 함수를 따로 만들어줘야한다.
// Main.cpp
#include <iostream>
#include <map>
#include <string>
class Score
{
public:
Score(int InID, float InValue)
: ID(InID)
, Value(InValue)
{
}
public:
int ID;
float Value;
};
int main()
{
std::map<Score, std::string> ScoreMap;
ScoreMap.insert(std::pair<Score, std::string>(Score(100, 95.f), "Kim"));
ScoreMap.insert(std::pair<Score, std::string>(Score(110, 70.f), "Lee"));
ScoreMap.insert(std::pair<Score, std::string>(Score(200, 98.f), "Park"));
return 0;
}
반드시 두 키를 비교하는 함수를 만들어야한다.
- operator<()
- comparer
// Main.cpp
#include <iostream>
#include <map>
#include <string>
class Score
{
public:
Score(int InID, float InValue)
: ID(InID)
, Value(InValue)
{
}
// 1. operator<()를 재정의하는 방법.
bool operator<(const Score& InScore) const
{
if (ID == InScore.ID)
{
return Value < InScore.Value;
}
return ID < InScore.ID;
}
public:
int ID;
float Value;
};
// 2. Comparer 구조체를 만들어서 operator()를 재정의하는 방법.
struct ScoreComparer
{
bool operator()(const Score& InScore1, const Score& InScore2) const
{
if (InScore1.ID == InScore2.ID)
{
return InScore1.Value > InScore2.Value; // 내림차순
}
return InScore1.ID > InScore2.ID;
}
};
int main()
{
std::map<Score, std::string> ScoreMap01;
ScoreMap01.insert(std::pair<Score, std::string>(Score(100, 95.f), "Kim"));
ScoreMap01.insert(std::pair<Score, std::string>(Score(200, 98.f), "Park"));
ScoreMap01.insert(std::pair<Score, std::string>(Score(110, 70.f), "Lee"));
{
std::map<Score, std::string>::const_iterator Iter = ScoreMap01.begin();
while (Iter != ScoreMap01.end())
{
std::cout << "ID: " << Iter->first.ID << ", Value: " << Iter->first.Value << ", Name: " << Iter->second << std::endl;
++Iter;
}
std::cout << "Size: " << ScoreMap01.size() << std::endl << std::endl;
}
std::map<Score, std::string, ScoreComparer> ScoreMap02;
ScoreMap02.insert(std::pair<Score, std::string>(Score(100, 95.f), "Kim"));
ScoreMap02.insert(std::pair<Score, std::string>(Score(110, 70.f), "Lee"));
ScoreMap02.insert(std::pair<Score, std::string>(Score(200, 98.f), "Park"));
{
std::map<Score, std::string, ScoreComparer>::const_iterator Iter = ScoreMap02.begin();
while (Iter != ScoreMap02.end())
{
std::cout << "ID: " << Iter->first.ID << ", Value: " << Iter->first.Value << ", Name: " << Iter->second << std::endl;
++Iter;
}
std::cout << "Size: " << ScoreMap02.size() << std::endl << std::endl;
}
return 0;
}
가끔 다른 프로그래머가 만든 클래스를 키로 사용해서 맵을 선언하는 경우도 있으므로 operator<() 함수를 해당 클래스에 마음대로 끼워넣기 애매할 수 있다. 이럴 때 comparer 개념을 활용한다.
unordered_map
키와 값의 쌍(pair)들을 저장한다.
키는 중복 저장이 불가능하다는 것까지는 map 컨테이너와 유사.
map은 키의 대소 비교로 탐색하지만 unordered_map은 키를 해시 함수의 인자로 넣어 계산된 해시값을 통해 탐색하는 해시 테이블 기반의 STL이다.
// Main.cpp
#include <iostream>
#include <string>
#include <unordered_map>
int main(void)
{
std::unordered_map<std::string, int> Scores;
Scores["Kim"] = 100;
Scores["Lee"] = 70;
Scores["Park"] = 99;
for (auto Iter = Scores.begin(); Iter != Scores.end(); ++Iter)
{
std::cout << Iter->first << " : " << Iter->second << std::endl;
}
return 0;
}
unorderd_map 컨테이너의 find() 함수
// Main.cpp
#include <iostream>
#include <string>
#include <unordered_map>
int main(void)
{
std::unordered_map<std::string, int> Scores;
Scores["Kim"] = 100;
Scores["Lee"] = 70;
Scores["Park"] = 99;
for (auto Iter = Scores.begin(); Iter != Scores.end(); ++Iter)
{
std::cout << Iter->first << " : " << Iter->second << std::endl;
}
auto FoundIter = Scores.find("Lee");
if (FoundIter != Scores.end())
{
std::cout << "Lee's score is " << FoundIter->second << std::endl;
}
return 0;
}
가끔 키가 다른데도 같은 해시값이 나올 수 있으며, 이를 해시 충돌이라고 한다.
이럴 경우 하나의 버킷에 둘 이상의 데이터가 들어간다.
/* main.cpp */
#include <iostream>
#include <string>
#include <unordered_map>
int main(void)
{
std::unordered_map<std::string, int> Scores;
Scores["Kim"] = 10;
Scores["Lee"] = 20;
Scores["Park"] = 30;
for (size_t i = 0; i < Scores.bucket_count(); ++i)
{
std::cout << "Bucket #" << i << ": ";
for (auto Iter = Scores.begin(i); Iter != Scores.end(i); ++Iter)
{
std::cout << ' ' << Iter->first << ':' << Iter->second;
}
std::cout << std::endl;
}
return 0;
}
map vs. unordered_map
| std::map | std::unordered_map |
| 자동으로 정렬되는 컨테이너 | 자동으로 정렬되지 않는 컨테이너 |
| 키-값 쌍들을 저장 | 키-값 쌍들을 저장 |
| 이진 탐색 트리 | 해시 테이블 |
| 탐색 시간이 O(logN) | 탐색 시간이 O(1) 해시 충돌시 최악의 경우 O(N) |
| 삽입과 제거가 빈번하면 느림. | 버킷 때문에 공간 복잡도 증가. |
'게임 개발 > 학습 일지' 카테고리의 다른 글
| TA 코스 1주차 정리 (0) | 2026.04.09 |
|---|---|
| 언리얼 C++ - 빌드 프로세스 이해하기 (0) | 2026.04.09 |
| TA 코스 게임 분석 과제 - 스컬걸즈 (0) | 2026.04.06 |
| 언리얼 C++기초 세팅 (0) | 2026.04.06 |
| Unreal 부트캠프 - C++ 블루프린트 클래스 만들기 과제 (0) | 2026.03.24 |