본문 바로가기
개발/C++ (98,03,11,14,17,20,23)

Modern C++ : std::vector (98, 11, 14, 17, 20)

by snowoods 2025. 8. 21.

Modern C++

C++ std::vector 컨테이너

 

개요

std::vector는 C++ 표준 라이브러리에서 제공하는 동적 배열 컨테이너로, 연속된 메모리 공간에 요소를 저장합니다. 자동으로 메모리를 관리하고 크기를 동적으로 조정할 수 있어 가장 널리 사용되는 컨테이너 중 하나입니다. 임의 접근이 가능하고, 끝에서의 삽입/삭제가 효율적입니다.

 

C++ 버전별 주요 키워드 도입 시기

  • C++98/03: 기본 std::vector 기능 도입
  • C++11: 이동 생성자/대입 연산자, emplace_back(), shrink_to_fit()
  • C++14: 일반화된 람다 캡처와 함께 사용 개선
  • C++17: 구조적 바인딩과 함께 사용 개선, emplace_back()이 참조 반환
  • C++20: std::span과의 호환성, std::ranges 지원

 

내용 설명

주요 특징

  1. 동적 배열: 연속된 메모리 공간에 요소 저장
  2. 자동 크기 조정: 필요에 따라 메모리를 자동으로 재할당
  3. 임의 접근: 인덱스를 통한 O(1) 시간 접근
  4. 끝에서의 효율적 연산: push_back(), pop_back()은 분할 상환 O(1) 시간
  5. 중간 삽입/삭제: O(n) 시간이 소요됨

 

주요 멤버 함수

  • size(): 요소 개수 반환
  • capacity(): 할당된 메모리 공간의 크기 반환
  • empty(): 컨테이너가 비었는지 확인
  • push_back(): 끝에 요소 추가
  • pop_back(): 마지막 요소 제거
  • insert(): 지정된 위치에 요소 삽입
  • erase(): 지정된 위치의 요소 제거
  • clear(): 모든 요소 제거
  • reserve(): 용량 미리 할당
  • shrink_to_fit(): 용량을 현재 크기에 맞춤

 

예제 코드

#include <cstdint>
#include <iostream>
#include <vector>

int main()
{
    // 크기 0으로 벡터 생성
    auto my_vec_empty = std::vector<std::int32_t>{};

    // 초기값을 가진 벡터 생성 (크기 5)
    auto my_vec = std::vector<std::int32_t>{1, 2, 3, 4, 5};

    // C 스타일 for 루프로 요소 접근
    std::cout << "\nC-Style Loop: \n";
    for (std::size_t i = 0; i < my_vec.size(); i++)
    {
        std::cout << my_vec[i] << '\n';
    }

    // 범위 기반 for 루프 (읽기 전용)
    std::cout << "\nC++ Ranged For Loop (read-only): \n";
    for (const auto value : my_vec)
    {
        std::cout << value << '\n';
    }

    // 범위 기반 for 루프 (값 수정)
    for (auto &value : my_vec)
    {
        value *= 2; // 모든 요소를 2배로
    }

    std::cout << "\nAfter doubling all values:\n";
    for (const auto value : my_vec)
    {
        std::cout << value << '\n';
    }

    // 크기 3의 벡터를 0으로 초기화하여 생성
    auto my_vec2 = std::vector<std::int32_t>(3, 0);
    std::cout << "\nVector with 3 zeros: \n";
    for (const auto value : my_vec2)
    {
        std::cout << value << '\n';
    }

    // 요소 추가 및 제거
    auto my_vec3 = std::vector<std::int32_t>{};
    my_vec3.push_back(10);
    my_vec3.push_back(22);
    std::cout << "\nAfter push_back(10) and push_back(22): \n";
    for (const auto value : my_vec3)
    {
        std::cout << value << '\n';
    }

    my_vec3.pop_back();
    std::cout << "\nAfter pop_back(): \n";
    for (const auto value : my_vec3)
    {
        std::cout << value << '\n';
    }

    // 반복자 사용
    auto it_begin = my_vec2.begin(); // 첫 번째 요소를 가리키는 반복자
    auto it_end = my_vec2.end();     // 마지막 요소 다음을 가리키는 반복자

    std::cout << "\nUsing Iterators: \n";
    for (; it_begin != it_end; ++it_begin)
    {
        std::cout << *it_begin << '\n';
    }

    // 요소 삽입
    my_vec2.insert(my_vec2.begin() + 1, 100);
    std::cout << "\nAfter inserting 100 at index 1: \n";
    for (const auto value : my_vec2)
    {
        std::cout << value << '\n';
    }

    return 0;
}

 

실행 결과

C-Style Loop: 
1
2
3
4
5

C++ Ranged For Loop (read-only): 
1
2
3
4
5

After doubling all values:
2
4
6
8
10

Vector with 3 zeros: 
0
0
0

After push_back(10) and push_back(22): 
10
22

After pop_back(): 
10

Using Iterators: 
0
0
0

After inserting 100 at index 1: 
0
100
0
0

 

활용팁

  1. 사전 할당: 많은 요소를 삽입할 계획이라면 reserve()로 메모리를 미리 할당하면 재할당 오버헤드를 줄일 수 있습니다.
  2. 요소 접근: at()은 범위 검사를 수행하지만, operator[]는 범위 검사를 하지 않아 더 빠릅니다.
  3. 반복자 무효화: 요소를 추가하거나 제거하면 반복자와 참조가 무효화될 수 있으니 주의가 필요합니다.
  4. 용량 관리: shrink_to_fit()을 사용하여 불필요한 메모리를 해제할 수 있지만, 재할당이 발생할 수 있습니다.
  5. 임시 객체: emplace_back()을 사용하면 임시 객체 생성을 피할 수 있어 더 효율적입니다.
  6. 데이터 연속성: 연속된 메모리 공간을 사용하므로 C 스타일 배열과의 호환성이 좋습니다.
  7. 크기 vs 용량: size()는 실제 요소 수를, capacity()는 할당된 메모리 공간을 반환합니다.
  8. 예외 안전: 대부분의 연산이 강력한 예외 안전성(strong exception safety)을 보장합니다.