공부/C++

C++ Vector Container 정리

상연 2020. 11. 1. 18:02

cplusplus 사이트 Reference를 참고하며 공부해봤습니다.

틀린 내용이 있다면 댓글로 지적 부탁드립니다.


Vector

  • 벡터는 크기가 변할 수 잇는 배열을 나타내는 'Sequence Container'이다.

    순차 컨테이너란? 원소가 들어오는 대로 저장한다는 의미, 별도의 정렬없이 들어오는 순서대로 저장된다.

  • 벡터는 배열과 마찬가지로 요소에도 연속적인 저장 위치를 사용한다.

    이는 해당 요소의 포인터의 오프셋을 사용하여 배열과 마찬가지로 효율적으로 요소에 접근할 수 있다.
    하지만, 선언하면서 저장공간을 정해줘야하는 배열과는 다르게 벡터는 동적으로 변경할 수 있다.

  • 내부적으로 벡터는 동적으로 할당된 어레이를 사용하여 요소를 저장한다.

    즉, 새 요소를 삽입할 때 크기가 커지기 위해 이 배열을 재할당해야 할 수도 있다.
    이는 새 배열을 할당하고 모든 요소를 새 배열에 이동해야 함을 의미한다. 하지만 이는 처리 시간의 측면에서 많은 비용이 들기 때문에 벡터는 하나의 요소가 추가 될 때마다 재 할당하지는 않는다.

  • 대신 여분의 저장공간을 미리 할당한다.

    따라서 벡터는 요소를 포함하는데 필요한 저장공간보다 실제 용량이 더 클 수 있다.

즉, 배열에 비해 벡터는 저장공간을 관리하고, 효율적으로 동적으로 확장할 수 잇는 대신 좀 더 많은 메모리를 소비한다.

다만, 다른 동적 순차 컨테이너(Deque, lists...) 에 비하면 벡터는 요소에 접근하는 것, 맨 앞과 뒤에 요소를 추가할 수 있는 점이 효율적이다. 대신에 요소 중간에 요소를 삽입하거나 제거하는 작업에서는 성능이 나쁜 편이다.

Vector 속성

  1. Sequence (순차)
  2. Dyanamic Array (동적배열)
  3. Allocator-aware (할당자 인식)

생산자

  1. vector<자료형> v;
    비어있는 vector v 생성

  2. vector<자료형> v(5);
    자료형의 기본값으로 초기화 된 5개의 원소를 가지는 vector v 생성

  3. vector<자료형> v(5, 지정값);
    지정값으로 초기화 된 5개의 원소를 가지는 vector v를 생성합니다.

  4. vector<자료형> v(iter1, iter2);
    iter1 ~ iter2 까지의 범위의 요소를 할당한 vector v를 생성합니다.

  5. vector<자료형> v(v2);
    같은 자료형인 v2의 요소를 복사하여 가진 vector v1를 생성합니다.

반복자

반복자를 통해 내부에 있는 데이터의 한 위치를 가리킬 수 있다.

이름 설명
begin 컨테이너의 시작 요소를 가리키는 반복자를 반환한다.
end 컨테이너의 시작 요소를 가리키는 반복자를 반환한다.
cbegin begin과 같지만, 반복자가 가리키는 대상을 수정할 수 없습니다.
cend end와 같지만, 반복자가 가리키는 대상을 수정할 수 없습니다.
vector<int>::iterator iter=v.begin();

만일 이렇게 iter에 벡터 v의 시작점을 저장했다고 하자.

  1. 전/후위 연산자

++iter 혹은 iter-- 등을 활용하여 반복자가 다음/이전 원소를 가리키도록 이동시킬 수 있다.

  1. *iter
    iter가 가리키는 요소(객체)를 반환한다.

  2. iter[n]
    iter + n 번째 요소(객체)를 반환한다.

  3. iter += n
    현재 iter위치에서 n개 뒤의 요소를 반환한다.

용량(?) Capacity

  1. v.size()
    컨테이너의 크기를 반환한다.(int 형)

  2. v.max_size()
    컨테이너가 가질 수 있는 가장 큰 수를 반환한다.(unsigned_int 형)

  3. v.resize()
    컨테이너 요소를 재조정한다.

/ resizing vector
#include <iostream>
#include <vector>

int main ()
{
  std::vector<int> myvector;

  // set some initial content:
  for (int i=1;i<10;i++) myvector.push_back(i);

  myvector.resize(5);
  myvector.resize(8,100);
  myvector.resize(12);

  std::cout << "myvector contains:";
  for (int i=0;i<myvector.size();i++)
    std::cout << ' ' << myvector[i];
  std::cout << '\n';

  return 0;
}

출력

myvector contains: 1 2 3 4 5 100 100 100 0 0 0 0

초기 벡터가

1 2 3 4 5 6 7 8 9 로 초기화

resize(5)로 인해

1 2 3 4 5 로 조정

resize(8, 100)로 인해

1 2 3 4 5 100 100 100 조정

resize(12)로 인해

1 2 3 4 5 100 100 100 0 0 0 0 으로 조정된 것이다.

  1. v.capacity()
    앞에서, 벡터는 미리 어느정도 용량을 확보한 상태로 동적배열을 운용한다고 했다.
    그렇다면 그 어느정도 확보한다는 기준은 어떻게 되는 것인가??

답은, 새로운 요소가 들어갈 공간이 없을때마다, 현재 저장공간의 2배로 공간을 늘리는 것이다.
이는 이 capacity 함수를 통해 알 수 있는데

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector <int> v;

    for(int i=0; i<100; i++){
        v.push_back(i);
        cout << i << "번째 Vector Size : " << v.size() << " Vector Capacity " << v.capacity() << endl;
    }
}

출력

0번째 Vector Size : 1 Vector Capacity 1
1번째 Vector Size : 2 Vector Capacity 2
2번째 Vector Size : 3 Vector Capacity 4
3번째 Vector Size : 4 Vector Capacity 4
4번째 Vector Size : 5 Vector Capacity 8
5번째 Vector Size : 6 Vector Capacity 8
6번째 Vector Size : 7 Vector Capacity 8
7번째 Vector Size : 8 Vector Capacity 8
8번째 Vector Size : 9 Vector Capacity 16
9번째 Vector Size : 10 Vector Capacity 16
10번째 Vector Size : 11 Vector Capacity 16
11번째 Vector Size : 12 Vector Capacity 16
12번째 Vector Size : 13 Vector Capacity 16
13번째 Vector Size : 14 Vector Capacity 16
14번째 Vector Size : 15 Vector Capacity 16
15번째 Vector Size : 16 Vector Capacity 16
16번째 Vector Size : 17 Vector Capacity 32
17번째 Vector Size : 18 Vector Capacity 32
18번째 Vector Size : 19 Vector Capacity 32
19번째 Vector Size : 20 Vector Capacity 32
20번째 Vector Size : 21 Vector Capacity 32
21번째 Vector Size : 22 Vector Capacity 32
22번째 Vector Size : 23 Vector Capacity 32
23번째 Vector Size : 24 Vector Capacity 32
24번째 Vector Size : 25 Vector Capacity 32
25번째 Vector Size : 26 Vector Capacity 32
26번째 Vector Size : 27 Vector Capacity 32
27번째 Vector Size : 28 Vector Capacity 32
28번째 Vector Size : 29 Vector Capacity 32
29번째 Vector Size : 30 Vector Capacity 32
30번째 Vector Size : 31 Vector Capacity 32
31번째 Vector Size : 32 Vector Capacity 32
32번째 Vector Size : 33 Vector Capacity 64
33번째 Vector Size : 34 Vector Capacity 64
34번째 Vector Size : 35 Vector Capacity 64
35번째 Vector Size : 36 Vector Capacity 64
36번째 Vector Size : 37 Vector Capacity 64
37번째 Vector Size : 38 Vector Capacity 64
38번째 Vector Size : 39 Vector Capacity 64
39번째 Vector Size : 40 Vector Capacity 64
40번째 Vector Size : 41 Vector Capacity 64
41번째 Vector Size : 42 Vector Capacity 64
42번째 Vector Size : 43 Vector Capacity 64
43번째 Vector Size : 44 Vector Capacity 64
44번째 Vector Size : 45 Vector Capacity 64
45번째 Vector Size : 46 Vector Capacity 64
46번째 Vector Size : 47 Vector Capacity 64
47번째 Vector Size : 48 Vector Capacity 64
48번째 Vector Size : 49 Vector Capacity 64
49번째 Vector Size : 50 Vector Capacity 64
50번째 Vector Size : 51 Vector Capacity 64
51번째 Vector Size : 52 Vector Capacity 64
52번째 Vector Size : 53 Vector Capacity 64
53번째 Vector Size : 54 Vector Capacity 64
54번째 Vector Size : 55 Vector Capacity 64
55번째 Vector Size : 56 Vector Capacity 64
56번째 Vector Size : 57 Vector Capacity 64
57번째 Vector Size : 58 Vector Capacity 64
58번째 Vector Size : 59 Vector Capacity 64
59번째 Vector Size : 60 Vector Capacity 64
60번째 Vector Size : 61 Vector Capacity 64
61번째 Vector Size : 62 Vector Capacity 64
62번째 Vector Size : 63 Vector Capacity 64
63번째 Vector Size : 64 Vector Capacity 64
64번째 Vector Size : 65 Vector Capacity 128
65번째 Vector Size : 66 Vector Capacity 128
66번째 Vector Size : 67 Vector Capacity 128
67번째 Vector Size : 68 Vector Capacity 128
68번째 Vector Size : 69 Vector Capacity 128
69번째 Vector Size : 70 Vector Capacity 128
70번째 Vector Size : 71 Vector Capacity 128
71번째 Vector Size : 72 Vector Capacity 128
72번째 Vector Size : 73 Vector Capacity 128
73번째 Vector Size : 74 Vector Capacity 128
74번째 Vector Size : 75 Vector Capacity 128
75번째 Vector Size : 76 Vector Capacity 128
76번째 Vector Size : 77 Vector Capacity 128
77번째 Vector Size : 78 Vector Capacity 128
78번째 Vector Size : 79 Vector Capacity 128
79번째 Vector Size : 80 Vector Capacity 128
80번째 Vector Size : 81 Vector Capacity 128
81번째 Vector Size : 82 Vector Capacity 128
82번째 Vector Size : 83 Vector Capacity 128
83번째 Vector Size : 84 Vector Capacity 128
84번째 Vector Size : 85 Vector Capacity 128
85번째 Vector Size : 86 Vector Capacity 128
86번째 Vector Size : 87 Vector Capacity 128
87번째 Vector Size : 88 Vector Capacity 128
88번째 Vector Size : 89 Vector Capacity 128
89번째 Vector Size : 90 Vector Capacity 128
90번째 Vector Size : 91 Vector Capacity 128
91번째 Vector Size : 92 Vector Capacity 128
92번째 Vector Size : 93 Vector Capacity 128
93번째 Vector Size : 94 Vector Capacity 128
94번째 Vector Size : 95 Vector Capacity 128
95번째 Vector Size : 96 Vector Capacity 128
96번째 Vector Size : 97 Vector Capacity 128
97번째 Vector Size : 98 Vector Capacity 128
98번째 Vector Size : 99 Vector Capacity 128
99번째 Vector Size : 100 Vector Capacity 128

  1. v.empty()
    해당 컨테이너가 비어있는지 아닌지 판단하여 bool형태의 자료형을 반환한다.
    비었으면 True, 비지 않았다면 False

  2. v.reverse()
    해당 컨테이너가 저장된 순서의 반대로 저장한다.

  1. v.shrink_to_fit (C++ 11 이상)
    이건 예제 코드를 보고 이해하는 것이 빠를 것 같다.
// vector::shrink_to_fit
#include <iostream>
#include <vector>

int main ()
{
  std::vector<int> myvector (100);
  std::cout << "1. capacity of myvector: " << myvector.capacity() << '\n';

  myvector.resize(10);
  std::cout << "2. capacity of myvector: " << myvector.capacity() << '\n';

  myvector.shrink_to_fit();
  std::cout << "3. capacity of myvector: " << myvector.capacity() << '\n';

  return 0;
}

이를 보면 처음 벡터는 100의 크기로 초기화 되어있었다.
하지만 10으로 resize 되면서 11~100까지의 요소는 사라졌는데, 그럼에도 할당된 용량은 100으로 그대로여서
대략 90의 용량이 낭비되어 있다는 것이다.

따라서 shrink_to_fit 을 해 주면
현재 사용하고 있는 요소에 맞춰 할당 용량을 맞춰준다고 보면 될 것 같다.

요소 접근자

  1. operator[]
    벡터 컨테이너의 위치 n에 있는 요소를 반환한다.
    ex) v[3] -> v 컨테이너의 4번째 요소 반환 (요소의 index는 0부터 시작함)

  2. at()
    operator[]와 유사한 기능을 수행한다.
    다만, 컨테이너의 범위를 점검하기 때문에 operator보다 안정성이 있다.

Vector at vs operator[]

  1. front(), back()
    이름 그대로 컨테이너의 첫번째, 마지막 요소를 반환한다.

수정자

  1. assign
    컨테이너에 새로운 내용을 넣는다. 이전에 있던 요소는 전부 사라지고 새로운 내용이 들어간다.
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v1(8, 2); // v1 = {2, 2, 2, 2, 2, 2, 2, 2}

    cout << " v1 : ";
    for(int i=0; i<v1.size(); i++){
        cout << v1[i];
    }
    cout << endl;

    cout << "v1의 capacity : " << v1.capacity() << endl;

    //assign의 첫 번째 방법 
    //첫번째 인자에 요소의 개수, 두번째 인자에 요소값
    //두번째인자의 값을 첫번째 인자의 개수만큼 채워넣는다.

    v1.assign(3, 2); // v1 = {2, 2, 2}

    cout << " v1 : ";
    for(int i=0; i<v1.size(); i++){
        cout << v1[i];
    }
    cout << endl;


    cout << "v1의 capacity : " << v1.capacity() << endl;

    //assign의 두 번째 방법
    //주어진 범위를 복사해서 요소를 옮긴다. 

    // 처음과 같이 초기화
    vector <int> v3(8, 2); // v3 = {2, 2, 2, 2, 2, 2, 2, 2}
    vector <int> v2(9, 3); // 새로운 벡터 v2 생성

    cout << " 3 : ";
    for(int i=0; i<v3.size(); i++){
        cout << v3[i];
    }
    cout << endl;
    cout << "v3의 capacity : " << v3.capacity() << endl;

    v3.assign(v2.begin(), v2.end());
    //v2의 첫번째 요소부터 마지막 요소까지 v1에 할당.

    cout << " v3 : ";
    for(int i=0; i<v3.size(); i++){
        cout << v3[i];
    }
    cout << endl;
    cout << "v3의 capacity : " << v3.capacity() << endl;
    // your code goes here
    return 0;
}

출력

v1 : 22222222
v1의 capacity : 8
v1 : 222
v1의 capacity : 8
3 : 22222222
v3의 capacity : 8
v3 : 333333333
v3의 capacity : 9

코드와 출력문을 보면 알 수 있지만
이전의 내용은 사라지고 새로 채워지지만, 요소가 줄어든다 해서 capacity가 변하진 않는다.

  1. push_back
    컨테이너의 마지막에 새로운 요소를 추가한다.
    요소를 추가할 때 현재 컨테이너의 용량을 초과하게 되는 경우에는, 현재 컨테이너 용량의 두배만큼의 컨테이너를 새로 할당한 후 기존의 요소를 복사한 후 새로운 요소가 추가된다.

  2. pop_back
    컨테이너의 마지막 요소를 제거한다.

  3. insert
    특정 위치에 요소를 추가한다.
    벡터는 배열이기 때문에 이러한 방법은 벡터에서 그리 효율적이진 않다.

insert 사용방법

  1. insert( 추가할 위치 인덱스(iter), 추가할 내용)
    지정한 위치에 원하는 요소를 추가한다.
  2. insert( 추가할 위치 인덱스(iter), 추가할 요소의 개수, 추가할 내용)
    지정한 위치부터 추가할 내용을 개수만큼 추가한다.
  3. insert( 추가할 위치 인덱스(iter), iter1, iter2)
    지정한 위치부터 다른 컨테이너의 iter1~iter2까지의 요소를 복사해와서 넣는다.
  1. erase
    특정 위치의 요소를 제거하거나, 범위내의 요소를 제거한다.
    iterator erase (const_iterator position);
    iterator erase (const_iterator first, const_iterator last);

  2. swap
    동일한 유형의 다른 벡터 컨테이너의 내용과 교체된다.

    // swap vectors
    #include <iostream>
    #include <vector>
    

int main ()
{
std::vector foo (3,100); // three ints with a value of 100
std::vector bar (5,200); // five ints with a value of 200

foo.swap(bar);

std::cout << "foo contains:";
for (unsigned i=0; i<foo.size(); i++)
std::cout << ' ' << foo[i];
std::cout << '\n';

std::cout << "bar contains:";
for (unsigned i=0; i<bar.size(); i++)
std::cout << ' ' << bar[i];
std::cout << '\n';

return 0;
}

```

출력

foo는 다음을 포함합니다 : 200200200200200
막대 포함 : 100100100

  1. clear
    벡터에서 모든 요소를 제거하고 컨테이너 크기를 0으로 유지한다.

1학기때 이렇게 공부했으면 C++ A+ 받았을 거 같은데,,, 이미 좀 늦은 것 같다.
cplusplus.com Reference를 참고했다.