프로그래밍/C++

C++ 표준 템플릿 라이브러리(STL): 컨테이너, 반복자, 알고리즘 종합 가이드

shimdh 2025. 2. 3. 10:31
728x90

1. 컨테이너(Container)

컨테이너는 데이터를 저장하고 관리하는 구조를 제공합니다. STL의 컨테이너는 크게 시퀀스 컨테이너, 연관 컨테이너, 비순차적 컨테이너로 나뉩니다. 각각의 컨테이너는 고유한 특성과 사용 목적을 가지고 있으며, 상황에 맞게 적절히 활용할 수 있습니다.

1.1 시퀀스 컨테이너

시퀀스 컨테이너는 데이터를 순서대로 저장하며, 인덱스를 사용하여 접근할 수 있는 구조입니다. 대표적인 시퀀스 컨테이너에는 vector, deque, list가 있습니다.

1.1.1 vector

vector는 동적 배열로, 크기가 가변적이며 빠른 랜덤 액세스를 지원합니다. 이는 주로 데이터를 효율적으로 추가하거나 관리해야 할 때 사용됩니다.

#include <iostream>
#include <vector>

int main() {
    std::vector<int> numbers = {1, 2, 3};
    numbers.push_back(4); // 동적으로 크기 증가
    for (int num : numbers) {
        std::cout << num << " "; // 출력: 1 2 3 4
    }
    return 0;
}

1.1.2 deque

deque는 양쪽 끝에서 삽입과 삭제가 가능하며, 양방향 데이터 처리에 적합합니다. 이는 큐(queue)나 덱(deque) 구조를 구현할 때 유용합니다.

1.1.3 list

list는 이중 연결 리스트로, 특정 위치의 삽입 및 삭제가 빠릅니다. 삽입과 삭제가 빈번한 작업에서 적합한 컨테이너입니다.

1.2 연관 컨테이너

연관 컨테이너는 키와 값의 쌍으로 데이터를 저장하며, 특정 키를 사용하여 값을 검색하거나 조작할 수 있습니다. 내부적으로는 이진 검색 트리와 같은 자료구조를 활용하여 정렬과 검색이 효율적입니다.

1.2.1 map

map은 키와 값의 쌍으로 정렬된 데이터를 저장합니다. 예를 들어, 이름과 나이를 저장하는 데 활용할 수 있습니다.

#include <iostream>
#include <map>

int main() {
    std::map<std::string, int> ageMap;
    ageMap["Alice"] = 30;
    ageMap["Bob"] = 25;

    for (const auto& pair : ageMap) {
        std::cout << pair.first << ": " << pair.second << "\n"; // 출력: Alice: 30 \n Bob: 25
    }
    return 0;
}

1.2.2 set

set은 고유한 값만 저장하며, 중복된 값을 자동으로 제거합니다. 이는 정렬된 데이터 관리와 중복 제거에 적합합니다.

1.3 비순차적 컨테이너

비순차적 컨테이너는 데이터를 해시 기반으로 관리하여 평균적으로 매우 빠른 접근 속도를 제공합니다.

1.3.1 unordered_map

unordered_map은 키-값 쌍을 저장하며, 해시 테이블 기반으로 구현됩니다. 데이터 정렬보다는 빠른 검색이 중요한 상황에서 사용됩니다.

#include <iostream>
#include <unordered_map>

int main() {
    std::unordered_map<std::string, int> umap;
    umap["apple"] = 3;
    umap["banana"] = 5;

    for (const auto& pair : umap) {
        std::cout << pair.first << ": " << pair.second << "\n";
    }
    return 0;
}

2. 반복자(Iterator)

반복자는 컨테이너의 요소를 순회하거나 조작할 수 있게 해주는 객체입니다. 이를 통해 데이터를 일관되게 처리할 수 있으며, 반복자는 STL의 강력한 유연성을 대표합니다.

2.1 반복자의 주요 특징

  1. 일관성: 모든 STL 컨테이너에서 동일한 인터페이스를 사용하여 요소를 순회하거나 접근할 수 있습니다.
  2. 유연성: 반복자는 컨테이너의 요소를 읽거나 수정할 수 있으며, 다양한 연산을 지원합니다.
  3. 추상화: 반복자를 사용하면 데이터 구조의 내부 구현을 몰라도 고수준의 작업이 가능합니다.

2.2 반복자의 유형

  • 입력 반복자: 데이터를 읽기 위해 사용됩니다.
  • 출력 반복자: 데이터를 쓰기 위해 사용됩니다.
  • 전방 반복자: 단방향으로만 이동 가능하며, 읽기 및 쓰기가 가능합니다.
  • 양방향 반복자: 앞뒤로 이동 가능하며, 이중 연결 리스트 등에서 사용됩니다.
  • 무작위 액세스 반복자: 임의 위치에 직접 접근이 가능하며, 벡터 및 배열에서 활용됩니다.

2.3 반복자 활용 예제

#include <iostream>
#include <vector>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    // 정방향 순회
    std::cout << "정방향:" << std::endl;
    for (std::vector<int>::iterator it = numbers.begin(); it != numbers.end(); ++it) {
        std::cout << *it << " ";
    }

    // 역방향 순회
    std::cout << "\n역방향:" << std::endl;
    for (std::vector<int>::reverse_iterator rit = numbers.rbegin(); rit != numbers.rend(); ++rit) {
        std::cout << *rit << " ";
    }

    return 0;
}

3. 알고리즘(Algorithm)

STL의 알고리즘은 컨테이너 데이터를 처리하거나 변환하는 데 필요한 다양한 함수를 제공합니다. 이 알고리즘들은 컨테이너와 독립적으로 동작하며, 컨테이너의 반복자를 입력으로 받아 작동합니다.

3.1 주요 알고리즘

3.1.1 정렬(Sorting)

  • std::sort: 주어진 범위의 요소를 정렬합니다. 기본적으로 오름차순 정렬이며, 사용자 정의 비교 함수도 지원합니다.

3.1.2 검색(Search)

  • std::find: 특정 값을 검색합니다. 값이 발견되면 해당 반복자를 반환합니다.
auto it = std::find(nums.begin(), nums.end(), 3);
if (it != nums.end()) {
    std::cout << "찾음: " << *it << '\n';
}

3.1.3 변환(Transform)

  • std::transform: 입력 범위의 각 요소에 함수를 적용하여 출력 범위에 저장합니다.
std::transform(nums.begin(), nums.end(), nums.begin(), [](int x) { return x * x; });

3.1.4 집계(Aggregation)

  • std::accumulate: 주어진 범위의 요소들의 합계를 계산합니다.
#include <numeric>
int sum = std::accumulate(nums.begin(), nums.end(), 0);

3.2 필터링 예제

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5};
    auto new_end = std::remove_if(nums.begin(), nums.end(), [](int x) { return x % 2 == 0; });
    nums.erase(new_end, nums.end());

    for (const auto& num : nums) {
        std::cout << num << " "; // 출력: 1 3 5
    }

    return 0;
}

결론

STL은 C++ 프로그래밍에서 필수적인 라이브러리로, 컨테이너, 반복자, 알고리즘을 통해 효율적이고 재사용 가능한 코드를 작성할 수 있게 해줍니다. 이를 잘 활용하면 코드의 가독성과 유지보수성이 크게 향상됩니다. STL을 학습하고 다양한 프로젝트에서 이를 적극적으로 적용하면, 개발자로서의 역량을 크게 향상시킬 수 있습니다.

STL은 단순한 라이브러리를 넘어, C++ 개발에서의 생산성과 효율성을 극대화할 수 있는 필수 도구입니다. 특히 알고리즘과 반복자의 결합은 복잡한 문제를 단순하고 우아하게 해결하는 데 중요한 역할을 합니다. 학습 초기에는 각 컨테이너와 알고리즘의 특성을 이해하는 데 집중하고, 이후 실습을 통해 다양한 상황에서 이를 적용해보는 것이 좋습니다. 이를 통해 더욱 정교하고 효율적인 코드를 작성할 수 있을 것입니다.

728x90