벡터

3 minute read


벡터(vector)

c++ 표준 라이브러리에 있는 클래스로 가변적으로 변하는 배열이라 할 수 있다. 동적으로 원소를 추가할 수 있고 그 만큼 크기가 자동으로 늘어나기 때문에 편리하게 사용이가능하다.
속도적인 측면에서는 배열보다 느리지만 메모리를 더 효율적으로 관리할 수 있는 장점이 있다. 이러한 저장 공간들을 컨테이너라고 부른다.


벡터의 구조

vector

front() : 첫 번째 원소
back() : 마지막 원소
begin() : 첫 번째 위치
end() : 마지막 다음 위치
size() : 원소의 개수
capacity() : 할당된 공간의 크기


벡터 사용

  • 선언
    std::vector<자료형> 이름;

      // <vector> 헤더파일을 필요로한다.
      // std:: 네임스페이스가 있기 때문에 유의한다.
      #include <vector>
      using std::vector;
    
      // 선언
      vector<int> v1;
    
      // 초기화
      // size가 3인 벡터 선언, 0으로 초기화 된다.
      vector<int> v2(3);
    
      // 초기화할 값을 정할 수 있다.
      // size가 3이고 요소들은 1로 초기화 된다.
      vector<int> v3(3, 1);
    
      // 요소의 수만큼 size 정해지고 각 값으로 초기화 된다.
      vector<int> v4 = { 1, 2, 3, 4, 5 };
    
      // 배열로 선언 가능하다.
      // 행은 가변이지만 열은 고정이 된다.
      vector<int> v5[] = { {1,2}, {3,4} };
    
      // 2차원 벡터
      // 행과 열 모두 가변이다.
      vector<vector<int>> v2d(10);  
    
  • 접근
    벡터의 인덱스에 접근하는 방법은 배열 처럼 접근하는 것과 함수를 사용하는 방법이 있다.

      v2[0] = 10;
      v2.at(1) = 11;
    

    같은 역할이지만 차이점이 존재한다. at은 함수내에서 size()를 확인하기 때문에 속도가 느리지만 안정성이 높아 디버깅에 용이하고, 오퍼레이터[]는 속도는 빠르나 에러를 발생시킬 수 있다.

  • 벡터의 크기에 대해서
    벡터를 선언할 때 요소의 수를 정할 수 있다. 이 때 벡터의 전체 용량은 이 size와 동일하며 push_back을 통해 size가 늘어나면 capacity도 자동으로 늘어나고, pop_back으로 size가 줄어들어도 늘어난 용량은 그대로 유지된다.

    push_back을 할 때 늘어나는 capacity는 불규칙적으로 늘어나는 듯 보인다.

    메모리를 실질적으로 차지하는 공간은 vector의 capacity만큼이다. 그렇다면 많은 공간이 필요해서 할당했다가 더 이상 쓰지않게 된다면 그 만큼 크기를 줄여야지 효율적일 것이다.

    size의 경우 pop_back(), clear() 등 함수로 크기를 조절할 수 있지만 capacity의 경우 늘어난 상태 그대로이다.

    capacity의 크기와 관련된 함수 reserve()의 경우도 현재보다 크게 할당할 수 있어도 줄이는 것은 불가능하다.

  • capacity 크기 조절

    • swap()함수
      임시로 원하는 크기만큼의 벡터를 새로 만들고 그 공간으로 현재 벡터를 옮겨 담는 방법이다.

        vector<int> v(100);
      
        for (int i = 0; i < 50; i++)
        {
            v.pop_back();
        }
      
        // size 50, capacity 100
      
        vector<int> tmp(50);
        tmp.swap(v);
      
        // size 50, capacity 50;
      
    • shrink_to_fit() 함수
      현재 size 크기로 capacity 크기를 맞춘다.
        v.shrink_to_fit();
      
    • swap() vs shrink_to_fit() ? 벡터의 버퍼 크기를 제어하기 위해서 이전부터 swap을 사용해 왔다. c++11에 들어서야 해당 기능을 제공하는 shrink_to_fit 함수가 추가되었는데 메모리를 재할당하는데 사용되는 비용에 관련해서 이슈가 있다고 한다. 어쨋든 컴파일러간 호환성을 생각하면 swap을 사용하는게 안정성이 있는데 둘 중 편한거 사용해도 크게 문제는 없을 거 같다.


벡터의 함수들

함수 기능
v.push_back() 벡터의 마지막 요소 뒤에 새로운 요소 추가
v.insert(위치, 값) 위치에 값을 삽입
v.emplace_back() 마지막 요소 뒤에 새로운 요소 추가
v.emplace(위치, 값) 위치에 값을 삽입
v.pop_back() 벡터의 마지막 요소 제거
v.erase(위치)
v.erase(시작 위치, 끝 위치)
위치에 있는 요소를 제거한다
v.clear() 벡터의 모든 요소를 지움
v.resize(크기) 벡터의 size를 크기만큼 조정
v.swap(벡터) v와 벡터를 스왑한다.
v.shrink_to_fit() capacity크기를 size크기로 맞춤
v.begin() 벡터의 시작점의 값을 반환
v.end() 벡터의 끝점의 값을 반환
v.rbegin() 벡터의 뒤에서부터 시작점의 값 반환
v.rend() 벡터의 뒤에서부터 끝점의 값 반환
v.at(i) 벡터의 i번 째 요소 접근
v[i] 벡터의 i번 째 요소 접근
v.front() 벡터의 첫 번째 요소 접근
v.back() 벡터의 마지막 요소 접근
  • push_back vs emplace_back
    두 함수는 동일한 기능을 하지만 내부적으로 차이가 있다. 둘 중 뭐가 더 효율적이라는 표현보다는 적절한 상황이 서로 다르다고 할 수 있다.

    자세한 내용은 이 글을 참고한다.


반복자(iterator)

반복자는 포인터와 비슷한 것으로 벡터같은 컨테이너에 저장된 원소들을 참조하기 위해서 사용한다.

컨테이너에 저장된 원소를 순회하면서 접근할 수 있는 역할을 하며 컨테이너를 순회하는 방식이 알고리즘마다 다르기 때문에 반복자도 여러 종류가 있게 된다.

  • 선언
    컨테이너::iterator 이름;

      vector<int>::iterator iter;
    
  • 순회
    반복자와 반복문으로 컨테이너 순회

      for( iter = v.begin(); iter != v.end(); ++iter)
      {
          ~
      }
    

    ++iter : 컨테이너의 다음 원소를 가리킨다.
    * iter : iter가 가리키는 원소를 반환한다.
    iter[i] : i번 째 원소를 반환한다.