Post

STL(Standard Template Library)

STL(Standard Template Library)

STL(Standard Template Library)

C++의 표준 템플릿 라이브러리(STL) 는 다양한 자료구조와 알고리즘을 제공하는 라이브러리

Sequence Containers

  • std::vector: 동적 배열로, 크기가 가변적이며, 요소에 대한 빠른 접근이 가능함.
    • 주요 함수:
      • push_back(value): 벡터의 끝에 요소 추가
      • pop_back(): 벡터의 마지막 요소 제거
      • size(): 벡터의 크기 반환
      • at(index): 인덱스에 해당하는 요소 반환 (범위 검사 포함)
      • clear(): 벡터의 모든 요소 제거
      • empty(): 벡터가 비어있는지 확인
    • 초기화:
      • std::vector<int> vec(10); // 크기가 10인 벡터 생성, 모든 요소는 0으로 초기화됨
      • std::vector<int> vec = {1, 2, 3}; // 초기값으로 벡터 생성
    • 비교:
      • std::vector<int> vec1 = {1, 2, 3};
      • std::vector<int> vec2 = {1, 2, 3};
      • vec1 == vec2 // true, 두 벡터의 요소가 동일함(크기와 내용 모두 비교)
  • std::array: 고정 크기의 배열로, 크기가 컴파일 타임에 결정됨.
    • 주요 함수:
      • size(): 배열의 크기 반환
      • at(index): 인덱스에 해당하는 요소 반환 (범위 검사 포함)
      • fill(value): 모든 요소를 지정한 값으로 초기화
    • 초기화:
      • std::array<int, 5> arr = {1, 2, 3, 4, 5}; // 크기가 5인 배열 생성
    • 비교:
      • std::array<int, 3> arr1 = {1, 2, 3};
      • std::array<int, 3> arr2 = {1, 2, 3};
      • arr1 == arr2 // true, 두 배열의 요소가 동일함

Iterators

  • Iterator는 STL 컨테이너의 요소에 접근하기 위한 객체로, 포인터와 유사한 역할을 함. 인덱싱이 불가능한 컨테이너에서도 요소에 접근할 수 있게 해줌.
  • 예시:
    1
    2
    3
    4
    
    std::vector<int> vec = {1, 2, 3, 4, 5};
    for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " "; // 요소 출력
    }
    
  • v.begin(): 컨테이너의 첫 번째 요소를 가리키는 iterator 반환
  • v.end(): 컨테이너의 마지막 요소 다음을 가리키는 iterator 반환
  • 더하기/빼기 연산을 통해 iterator를 이동할 수 있음.
  • 비교 연산자(==, !=)를 사용하여 iterator의 위치를 비교할 수 있음.
  • *it: iterator가 가리키는 요소에 접근하기 위해 역참조 연산자를 사용함.
  • reverse iterator: std::reverse_iterator를 사용하여 컨테이너의 요소를 역순으로 순회할 수 있음.
    1
    2
    3
    4
    
    std::vector<int> vec = {1, 2, 3, 4, 5};
    for (std::vector<int>::reverse_iterator it = vec.rbegin(); it != vec.rend(); ++it) {
        std::cout << *it << " "; // 역순으로 요소 출력
    }
    
  • 포인터와 유사하게 동작하지만, 실제 포인터는 아님

Algorithms

  • STL은 다양한 알고리즘을 제공하여 컨테이너의 요소를 처리할 수 있음.
  • 예시:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    #include <algorithm>
    #include <vector>
    #include <iostream>
    
    int main() {
        std::vector<int> vec = {5, 3, 8, 1, 2};
          
        // 정렬
        std::sort(vec.begin(), vec.end());
        for (int v : vec) {
            std::cout << v << " "; // 1 2 3 5 8 출력
        }
          
        // 최대값 찾기
        int maxVal = *std::max_element(vec.begin(), vec.end());
        std::cout << "\nMax value: " << maxVal; // Max value: 8 출력
        
        // 특정 값 찾기
        auto it = std::find(vec.begin(), vec.end(), 3);  
        if (it != vec.end()) {
            std::cout << "\nFound 3 at index: " << std::distance(vec.begin(), it); // Found 3 at index: 1 출력
        } else {
            std::cout << "\n3 not found";
        }
    
        // 복사
        std::vector<int> copyVec;
        std::copy(vec.begin(), vec.end(), std::back_inserter(copyVec));
        return 0;
    }
    

Pair and Tuple

  • 만약 반환을 두 개 이상의 값으로 하고 싶다면, std::pair 또는 std::tuple을 사용할 수 있음.
  • std::pair는 두 개의 값을 저장하는 컨테이너로, std::make_pair 함수를 사용하여 생성할 수 있음.
  • 예시:
    1
    2
    3
    4
    5
    6
    7
    8
    
    #include <iostream>
    #include <utility> // std::pair, std::make_pair
    
    int main() {
        std::pair<int, std::string> p = std::make_pair(1, "Hello");
        std::cout << "First: " << p.first << ", Second: " << p.second << std::endl; // First: 1, Second: Hello 출력
        return 0;
    }
    
  • std::tuple은 세 개 이상의 값을 저장할 수 있는 컨테이너로, std::make_tuple 함수를 사용하여 생성할 수 있음.
  • 예시:
    1
    2
    3
    4
    5
    6
    7
    8
    
    #include <iostream>
    #include <tuple> // std::tuple, std::make_tuple
    
    int main() {
        std::tuple<int, double, std::string> t = std::make_tuple(1, 3.14, "World");
        std::cout << "First: " << std::get<0>(t) << ", Second: " << std::get<1>(t) << ", Third: " << std::get<2>(t) << std::endl; // First: 1, Second: 3.14, Third: World 출력
        return 0;
    }
    

Map and Set

  • std::map: 키-값 쌍을 저장하는 컨테이너로, 키를 기준으로 정렬됨. 중복된 키는 허용되지 않음.
    • 예시:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      
      #include <iostream>
      #include <map>
      
      int main() {
          std::map<std::string, int> m;
          m["apple"] = 1;
          m["banana"] = 2;
          m["orange"] = 3;
      
          for (const auto& pair : m) {
              std::cout << pair.first << ": " << pair.second << std::endl; // apple: 1, banana: 2, orange: 3 출력
          }
          return 0;
      }
      
    • 주요 함수:
      • insert({key, value}): 키-값 쌍을 삽입
      • find(key): 키에 해당하는 요소를 찾음
      • erase(key): 키에 해당하는 요소를 제거
      • clear(): 모든 요소 제거
      • size(): 맵의 크기 반환
      • empty(): 맵이 비어있는지 확인
  • std::set: 중복되지 않는 요소를 저장하는 컨테이너로, 요소는 자동으로 정렬됨.
    • 예시:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      
      #include <iostream>
      #include <set>
      
      int main() {
          std::set<int> s;
          s.insert(1);
          s.insert(2);
          s.insert(3);
          s.insert(2); // 중복된 요소는 무시됨
      
          for (const auto& elem : s) {
              std::cout << elem << " "; // 1 2 3 출력
          }
          return 0;
      }
      
    • 주요 함수:
      • insert(value): 요소를 삽입
      • find(value): 요소를 찾음
      • erase(value): 요소를 제거
      • clear(): 모든 요소 제거
      • size(): 집합의 크기 반환
      • empty(): 집합이 비어있는지 확인

Stack and Queue

  • std::stack: LIFO(Last In First Out) 구조로, 마지막에 추가된 요소가 가장 먼저 제거됨.
    • 예시:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      
      #include <iostream>
      #include <stack>
      
      int main() {
          std::stack<int> s;
          s.push(1);
          s.push(2);
          s.push(3);
      
          while (!s.empty()) {
              std::cout << s.top() << " "; // 3 2 1 출력
              s.pop(); // 가장 위의 요소 제거
          }
          return 0;
      }
      
    • 주요 함수:
      • push(value): 스택에 요소 추가
      • pop(): 스택에서 가장 위의 요소 제거(반환하지 않음)
      • top(): 스택의 가장 위의 요소 반환
      • empty(): 스택이 비어있는지 확인
      • size(): 스택의 크기 반환
  • std::queue: FIFO(First In First Out) 구조로, 가장 먼저 추가된 요소가 가장 먼저 제거됨.
    • 예시:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      
      #include <iostream>
      #include <queue>
      
      int main() {
          std::queue<int> q;
          q.push(1);
          q.push(2);
          q.push(3);
      
          while (!q.empty()) {
              std::cout << q.front() << " "; // 1 2 3 출력
              q.pop(); // 가장 앞의 요소 제거
          }
          return 0;
      }
      
    • 주요 함수:
      • push(value): 큐에 요소 추가
      • pop(): 큐에서 가장 앞의 요소 제거(반환하지 않음)
      • front(): 큐의 가장 앞의 요소 반환
      • back(): 큐의 가장 뒤의 요소 반환

Library

실행 가능한 프로그램이 만들어지는 과정은

  • 소스 코드 작성 -> 컴파일러를 통한 컴파일 -> 오브젝트 파일 생성 -> 링커를 통해 오브젝트 파일과 라이브러리를 결합하여 실행 파일 생성
  • 링커는 오브젝트 파일들을 결합하는 것으로, 라이브러리도 오브젝트 파일의 집합으로 볼 수 있음.
  • C++ 라이브러리는 두가지로 구성됨
    • 헤더 파일: 함수의 선언부를 포함하고 있는 파일로, 컴파일러가 함수의 정의를 찾을 수 있도록 도와줌.
    • 컴파일된 라이브러리 파일: 함수의 정의가 포함되어 있는 파일로, 링커가 실행 파일을 생성할 때 사용함.
      • 라이브러리는 거의 바뀌지 않으므로, 미리 컴파일된 라이브러리 파일을 제공함.
  • 라이브러리를 사용하는 방법은 크게 두 가지로 나뉨
    • 정적 라이브러리: 컴파일 시 링커가 오브젝트 파일을 결합하여 실행 파일을 생성함. 이때 라이브러리의 오브젝트 파일이 실행 파일에 포함됨.
      • 확장자는 .a 또는 .lib
    • 동적 라이브러리: 컴파일시 링커가 오브젝트 파일을 참조만 하고, 런타임에 라이브러리를 로드하여 사용함. 이때 라이브러리의 오브젝트 파일은 실행 파일에 포함되지 않음.
      • 확장자는 .so(Linux), .dll(Windows)
  • 정적 라이브러리의 장점
    • 쓰기 쉽다
    • 컴파일 하고 나면 실행 파일에 포함되므로, 별도의 라이브러리 파일이 필요하지 않음
  • 정적 라이브러리의 단점
    • 실행 파일의 크기가 커짐
    • 라이브러리를 업데이트하려면 다시 컴파일해야 함

해당 포스트는 서울대학교 전기정보공학부 정교민 교수님의 프로그래밍방법론 25-1학기 강의를 정리한 내용입니다.

This post is licensed under CC BY 4.0 by the author.