[STL 컨테이너] vector

Date:     Updated:

카테고리:

태그:

#include <vector>

🔔 vector 컨테이너 소개

원소들이 순서(인덱스)를 가지며 선형으로 배열되어 있는 Sequence Container

  • 동적 배열로 구현되어 있다.
    • 힙 메모리를 쓰지만 개발자가 직접 delete해주지 않아도 된다. 사용되지 않으면 알아서 메모리 해제함.
  • Sequence Container이므로
    • index만으로 탐색할 원소에 바로 참조가 가능하다.
      • \(O(1)\) 시간 소요
    • 순서대로 접근이 가능하다.
      • \(O(N)\) 시간 소요
  • 원소 추가
    1. push_back으로 벡터의 오른쪽 끝에 추가
    2. insert으로 원하는 곳 중간에 삽임 by 반복자
  • C++ 표준인 vector.h 에 템플릿 클래스로 정의가 되어 있는데, 특별히 bool은 특수화가 되어 있다.
    • bool일때는 메모리를 줄이기 위해 한개의 비트만 사용하도록 설계되어 있음.
      • 원래 C++의 최소 자료형 크기는 8 비트

다른 컨테이너들과 비교

  • list와 다르게 일반 정적 배열처럼 벡터 컨테이너 원소들도 메모리상에서 연속적으로 존재한다.
  • 정적 배열과는 다르게 스스로 공간을 할당하고 크기를 확장하고 줄일 수 있다.
    • 그래서 좀 더 여유있게 할당받기 때문에 (capacity) 정적 배열보다는 좀 더 많은 메모리를 필요로 한다.
  • 원소를 중간에 삽입하는 작업은 deque, list보다는 느리다.
    • 다만 끝에 삽입하는 것은 vector가 훨씬 빠르고 효율적.

capacity & size

new, delete 연산을 최대한 줄이기 위하여 원소 추가시 미리 할당 받은 capacity내에서 공간을 땡겨온다

  • capacity : 실제로 할당 받은 메모리. 여유 공간이라고 보면 된다. 항상 size보다 크게 조정된다.
  • size : 할당 받은 capacity 중에서 실제 원소로 사용되고 있는 크기. 즉 원소 개수.
  • 참고 : std::vector를 스택처럼 사용하기


🔔 함수

  • 정보
    • capacity : 벡터의 여유 공간 리턴
      • reserve(n) : capacity 를 n 으로 설정
    • size() : 벡터의 원소 개수 리턴
      • resize(n) : 원소 개수를 n 으로 설정
      • max_size() : 담을 수 있는 원소의 최대 크기를 리턴
    • data() : 벡터가 사용하는 힙 메모리 주소를 리턴
    • empty() : 빈 벡터인지에 대해 true, false 리턴
  • 반복자
    • begin() : 벡터의 첫번째 원소를 가리키는 반복자
    • end() : 벡터읨 마지막 원소의 다음을 가리키는 반복자. 즉 빈 곳을 가리키며 벡터의 끝을 나타낸다.
    • rbegin() : 역순. 즉, 벡터의 마지막 원소를 가리키는 반복자
    • rend() : 역순. 즉, 벡터의 첫번째 원소의 바로 이전을 가리키는 반복자. 즉 빈 곳을 가리키며 벡터가 시작되는 곳을 나타낸다.
  • 원소 접근
    • [n] : 연산자 오버로딩이 되어있어 [인덱스]로 바로 해당 원소에 접근할 수 있다.
      • == < 같은 비교 연산자 오버로딩도 되어 있어서 두 벡터의 원소들이 전부 같은지, 전부 큰지 등등 비교할 수도 있다.
      • = 연산자가 오버로딩 되어 있기 때문에 서로 다른 두 벡터끼리 =을 하면 a 벡터 = b 벡터 하면 a 벡터에 b 벡터의 원소들이 전부 복사된다. a 벡터가 b 벡터와 동일해짐.
    • at(n) : 해당 원소에 접근. []와 기능은 동일하나 []보다 느리다.
    • front() : 첫번째 원소에 접근한다.
    • back() : 마지막 원소에 접근한다.
  • 원소 추가
    • assign
      1. assign(반복자, 반복자)
        • 호출한 벡터의 해당 반복자 구간으로 할당한다.
      2. assign(n, x)
        • x 를 n 번만큼 반복하여 집어 넣는다.
    • push_back(원소) : 원소를 벡터의 끝에 추가한다.
      • emplace_back(가변인자)
        • push_back과 같이 원소를 벡터의 끝에 추가한다는 것은 같으나 벡터에 객체를 추가할 때 밖에서 생성한 후 인수로 넘겨주어야 하는 push_back과 다르게 가변 인자로 객체 생성에 필요한 인자만 받은 후 emplace_back 함수 내부에서 객체를 생성하여 벡터에 추가한다.
        • push_back보다 성능이 좋다.
    • insert(반복자, 원소) : 해당 반복자가 가리키는 곳에 원소를 삽입
      • insert(반복자, n, x) : 해당 반복자가 가리키는 곳에 x 원소를 n 개 만큼 반복하여 삽입한다. 즉 n 개 삽입.
  • 원소 삭제
    • pop_back(원소) : 마지막 원소를 제거한다. vec.erase(vec.end() - 1) 와 같다.
    • erase(반복자) : 해당 반복자가 가리키는 곳의 원소를 삭제
    • clear() : 벡터의 원소 전부 삭제
  • 원소 바꿔치기
    • swap(다른 벡터) : 다른 벡터와 원소를 바꿔치기 한다.
  • 할당자
    • get_allocator : 할당자를 얻는다.


🔔 벡터의 다양한 초기화 방법들

크기 & 디폴트 값

/* 1차원 벡터 */
vector<int> v1 = { 1, 2, 3 };
vector<int> v2;         // 벡터의 크기 0 
vector<int> v3(3);      // 벡터의 크기 3 (원소는 쓰레기 값)
vector<int> v4(4, 1);   // 벡터의 크기 4에 모두 1로 초기화 {1, 1, 1, 1}
/* 2차원 벡터 */
vector<vector<int>> v1;     // 벡터의 크기 0
vector<vector<int>> v2(3);  // 벡터의 크기 3  (3행 0열인 상태)
vector<vector<int>> v3;(3, vector<int>(3)); // 벡터의 크기 3행 3열
vector<vector<int>> v3;(3, vector<int>(3, 1)); // 벡터의 크기 3행 3열이며 모든 원소들이 1로 초기화된 상태

복사

/* vec1 기존 벡터를 복사하여 만들기 */
vector<int> vec1(3, 1);

vector<int> vec2(vec1);
vector<int> vec3(vec1.begin(), vec1.end());

map<int, int> m; 
vector<pair<int, int>> v(m.begin(), m.end()); // map을 복사. map은 pair를 원소로 가지니 가능.

벡터 초기화 주의 사항

아직 아무런 공간이 없는 벡터일 때 👉 push_back을 통해 삽입

내용물은 아직 없지만 공간은 잡혀있는 벡터일 때 👉 [] 연산자를 통해 삽입

    vector<char> vec = {'a', 'b', 'c', 'd'};
    vector<char> perm(r);
    
    vector<pair<char, bool>> check(vec.size());
    for(int i = 0; i < vec.size(); i++)  
        check.push_back(make_pair(vec[i], false)); // ❌런타임 에러 발생!

위와 같이 하면 에러가 발생하거나 공백들이 출력되는 등 정상적으로 출력되지 않는다. 왜 그럴까?

  • check는 vec.size() 사이즈 만큼의 공간을 이미 갖고 있는 상태다. vector<pair<char, bool>> check(vec.size()); 라고 선언해서!
  • 그런 상태에서 push_back을 해버리면 의미있는 내용물은 아직 없지만 vec.size() 사이즈만큼의 공간을 이미 차지하는 상태에서 뒤에 원소를 추가해주려고 하니 에러가 발생하는 것이다. 특히나 vecotr<int> 타입인 경우엔 자동으로 미리 잡아둔 해당 공간들을 0 으로 초기화 해주기 때문에 문제가 없을지 몰라도 vector<pair<char, bool>> 이렇게 pair나 혹은 사용자 정의 객체 타입을 원소로 사용하는 벡터일 경우엔 자동으로 어떤 값으로 초기화 할지 정보가 없기 때문에 초기화 되지 않아서 더더욱 문제가 생긴다.
vector<pair<char, bool>> check;
for(int i = 0; i < vec.size(); i++)  
    check.push_back(make_pair(vec[i], false)); 
  • vector<pair<char, bool>> check; 그냥 이렇게 사이즈 0 인 아무것도 없는 빈 공간의 컨테이너로 선언한 후 그 이후에 push_back으로 뒤에 추가해주면 문제가 발생하지 않는다.
vector<int> vec;

vec[0] = 2;  // ❌런타임 에러 발생!

벡터 vec은 아직 아무런 공간을 차지 하지 않는 벡터다. 그런 상황에서 vec[0]을 통해 없는 공간을 사용하려고 하면 위와같이 에러가 발생한다.

vector<int> vec(3);

vec[0] = 2;  

위와 같이 vec을 선언시 int 데이터가 3 개 들어갈 만큼 공간을 미리 확보해두면 vec[0]을 통해 접근해도 문제가 없다. 이미 vec로서 존재하는 공간에 접근하려고 하는 것이기 때문이다! 그리고 저렇게 미리 공간을 잡아두면 vec[0]~vec[2]는 0으로 초기화를 해준다. (int 벡터의 경우)



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

STL 카테고리 내 다른 글 보러가기

댓글 남기기