C++ STL: 11장 - STL 실전 활용과 추가 팁

2025. 2. 26. 18:21프로그래밍 언어/C++ STL

11.1 STL의 핵심 요약과 보충 설명

STL의 구성 요소를 깊이 이해하면 코드 품질과 효율성을 높이는 데 크게 기여할 수 있습니다. 이 장에서는 이전 장의 내용을 기반으로 추가적인 보충 설명과 실전 활용 팁을 제공합니다.

11.1.1 컨테이너 선택 기준 보충

컨테이너는 작업의 성격과 데이터 크기에 따라 선택해야 합니다. 아래는 컨테이너 선택 시 고려해야 할 주요 기준입니다:

  1. 삽입/삭제 빈도와 위치:
    • 끝에서의 삽입/삭제:
      • 추천: std::deque, std::list
      • 이유: std::deque는 여러 블록으로 구성되어 메모리 리얼로케이션 부담이 적고, std::list는 연결 리스트 구조로 끝에서의 삽입/삭제가 O(1)로 매우 효율적입니다.
    • 중간 또는 앞에서의 삽입/삭제:
      • 추천: std::list
      • 이유: 연결 리스트 구조로 중간 삽입/삭제가 O(1)로 효율적입니다.
    • 읽기 작업이 많고 삽입/삭제가 드문 경우:
      • 추천: std::vector
      • 이유: 연속 메모리 구조로 랜덤 접근이 빠릅니다. 다만, 메모리 리얼로케이션 비용이 있을 수 있습니다.
  2. 데이터 정렬 여부:
    • 항상 정렬된 상태를 유지하려면 std::set 또는 std::map.
    • 정렬 필요 없고, 빠른 검색이 중요하다면 std::unordered_set 또는 std::unordered_map.
  3. 메모리 제약:
    • 고정 크기 데이터를 다룰 경우 std::array.

11.1.2 이터레이터의 안전한 사용

  1. 무효화 주의:
    • 삽입/삭제 작업으로 인해 이터레이터가 무효화될 수 있으므로, 작업 후 새로 할당된 이터레이터를 사용해야 합니다.

예제:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3};
    auto it = vec.begin();
    vec.insert(it, 0); // 삽입으로 인해 이터레이터 무효화 발생

    // 올바르게 새로 이터레이터를 가져와야 함
    it = vec.begin();
    std::cout << *it << "\n";

    return 0;
}
  1. std::advance와 std::next 활용:
    • 이터레이터를 안전하게 이동시키는 도구로 활용 가능합니다.

예제:

#include <list>
#include <iostream>
#include <iterator>

int main() {
    std::list<int> lst = {1, 2, 3, 4, 5};
    auto it = lst.begin();

    std::advance(it, 2); // 2칸 앞으로 이동
    std::cout << *it << "\n";

    auto next_it = std::next(it); // 다음 요소 가져오기
    std::cout << *next_it << "\n";

    return 0;
}

11.2 STL 실전 활용 팁

11.2.1 효율적인 메모리 관리

  1. emplace 사용:
    • emplace는 객체를 직접 생성하여 삽입하는 방식으로, 복사 또는 이동 비용을 줄입니다.

예제:

#include <vector>
#include <iostream>
#include <string>

int main() {
    std::vector<std::string> vec;
    vec.emplace_back("Hello"); // 복사 비용 없이 직접 삽입

    for (const auto& str : vec) {
        std::cout << str << " ";
    }

    return 0;
}
  1. 메모리 할당 최소화:
    • std::vector와 std::deque에서 reserve를 사용하여 메모리 재할당을 줄입니다.

11.2.2 병렬 알고리즘과 STL

병렬 실행 정책(std::execution)을 사용하여 대규모 데이터 처리 시 성능을 극대화할 수 있습니다.

예제:

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

int main() {
    std::vector<int> vec(1000000, 1);

    std::for_each(std::execution::par, vec.begin(), vec.end(), [](int& x) {
        x += 1;
    });

    std::cout << vec[0] << "\n"; // 출력: 2
    return 0;
}

11.3 STL 활용 사례

11.3.1 텍스트 데이터 처리

상황: 텍스트 파일의 단어 빈도 계산.

해결책: std::unordered_map과 std::ifstream 활용.

예제:

#include <unordered_map>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>

int main() {
    std::ifstream file("text.txt");
    std::unordered_map<std::string, int> word_count;
    std::string word;

    while (file >> word) {
        ++word_count[word];
    }

    for (const auto& [key, value] : word_count) {
        std::cout << key << ": " << value << "\n";
    }

    return 0;
}

11.3.2 그래프 데이터 표현

상황: 그래프의 인접 리스트 표현.

해결책: std::vector와 std::pair 사용.

예제:

#include <vector>
#include <iostream>

int main() {
    using Edge = std::pair<int, int>;
    std::vector<std::vector<Edge>> graph(5);

    graph[0].emplace_back(1, 10); // 정점 0에서 정점 1로 가중치 10
    graph[1].emplace_back(2, 20);

    for (size_t i = 0; i < graph.size(); ++i) {
        std::cout << "Node " << i << ": ";
        for (const auto& edge : graph[i]) {
            std::cout << "(to: " << edge.first << ", weight: " << edge.second << ") ";
        }
        std::cout << "\n";
    }

    return 0;
}

11.4 추가 팁

  1. STL Cheatsheet 만들기:
    • 자주 사용하는 컨테이너와 알고리즘의 요약본을 작성하여 빠르게 참고할 수 있도록 준비합니다.
  2. 디버깅 도구 활용:
    • 이터레이터 범위 초과, 메모리 누수 등을 잡기 위해 ASAN(AddressSanitizer) 같은 도구를 사용합니다.
  3. STL과 현대 C++ 통합:
    • C++17/20의 새로운 기능(std::optional, std::variant, std::filesystem)을 STL과 결합하여 활용도를 높입니다.
  4. 병렬 프로그래밍과 결합:
    • std::thread, std::async를 사용하여 멀티스레드 환경에서 STL을 효과적으로 사용합니다.

결론

이 장에서는 STL의 활용도를 높이기 위한 추가 팁과 실전 사례를 다뤘습니다. STL은 단순한 라이브러리가 아니라, 현대 C++ 프로그래밍의 기본 도구로서 강력한 기능을 제공합니다.