본문 바로가기
C++ STL/Part 02 STL 이해

6장 시퀀스 컨테이너

by 노오오오오옹 2021. 6. 22.

1. vector 컨테이너

  • 백터의 주요 인터페이스와 특징
template<typename T, typename Allocator = allocator<T>> 
class Vector { ... }

백터는 원소의 저장 위치가 정해져 있는 배열(원소가 하나의 메모리) 기반의 컨테이너이다.

시퀀스 컨테이너의 특징인 push_back(), pop_back(), front(), back(), insert() 등의 함수가 있다.

push_back()시 자동으로 메모리가 할당된다.

 

  • 생성자
생성자 설명
vector v v는 빈 컨테이너다.
vector v(n) v는 기본값으로 초기화된 n개의 원소를 갖는다.
vector v(n,x) v는 x값으로 초기화된 n개의 원소를 갖는다.
vector v(v2) v는 v2 컨테이너의 복사본(복사 생성자)이다.
vector v(b, e) v는 반복자[begin, end)로 초기화된 원소다

 

  • 멤버함수 (p:반복자, q:p다음 반복자, b:반복자 시작, e:반복자 끝)
추가/할당 설명
v.assign(n, x) x값으로 n개를 할당한다
v.assign(b, e) [begin, end)까지의 값을 할당한다
v.push_back(x) 끝에 원소 x를 추가한다.
v.insert(p, x) p의 위치에 x를 삽입한다.
v.insert(p, n, x) p의 위치에 n개의 x값을 삽입한다
q = v.insert(p, b, e) p의 위치에 반복자 [b,e)를 삽입한다.
제거 설명
v.pop_back() 마지막 원소를 제거한다.
q = v.erase(p) 반복자에 해당하는 원소를 제거한다
q = v.erase(b,e) 반복자 [b,e)에 해당하는 원소들을 제거한다.
v.clear() 모든 원소를 제거한다.
참조/반복자 설명
v.at(i) i번째 원소를 참조(const, 비 const 상관 X)한다
v.front() 첫 번째 원소를 참조한다.
v.back() 마지막 원소를 참조한다.
p = v.begin() 첫번째 원소를 가르키는 반복자.
p = v.end() 마지막 원소를 가르키는 반복자.
p = v.rbegin() 역 순차열의 첫 원소를 가르키는 반복자
p = v.rend() 역 순차열의 마지막 원소를 가르키는 반복자
특수 설명
v.reserve(n) n개의 원소를 저장할 공간을 예약한다
x = v.capacity() 할당된 크기
기타 설명
v.resize(n) 크기를 n으로 변경하고, 기본값으로 초기화한다.
v.resize(n, x) 크기를 n으로 변경하고, x값으로 초기홯나다.
v.size() 원소의 개수
x = v.max_size() 담을 수 있는 최대 원소의 개수(메모리 크기)
v.empty() 비었는지 판단한다.
v.swap(v2) v와 v2를 스왑한다.

 

  • 연산자
연산자 설명
v1 == v2 v1과 v2의 모든 원소가 같은가?
v1 != v2 v1과 v2의 원소가 하나라도 다른가?
v1 < v2 v2가 v1보다 큰가?
v1 <= v2 v2가 v1 이상인가?
v1 > v2 v1이 v2보다 큰가?
v1 >= v2 v1이 v2 이상인가?
v[i] v의 i번재 원소를 참조한다. 범위를 점검하지 않음.

※ at(i)과 [i]의 차이: at은 범위를 점검하고, []는 점검하지 않아 오류가 발생할 수 있다.

 

  • 템플릿 멤버 형식
멤버 형식 설명
allocator_type 메모리 관리자 형식
const_iterator const 반복자 형식
const_ reverse_iterator const 역 반복자 형식
const_pointer const value_type* 형식
const_reference const value_type& 형식 
difference_type 두 반복자의 형식 차이
iteartor 반복자 형식
reverse_iterator 역 반복자 형식
pointer value_type* 형식
reference value_type& 형식
size_type 첨자나 원소의 개수 등의 형식
value_type 원소의 형식

// v.size()는 size_type이기 때문에 컴파일러 에러가 발생한다.
for(int i=0; i<v.size(); ++)

// typedef된 멤버 형식 size_type을 사용한다.
// v.size()를 int로 변환하거나, unsigned_int i=0 로 하는 방법도 있다.
for(vector<int>::size_type i=0; i<v.size(); ++)

  • 할당 및 변경

vector의 원소가 추가될 때 마다 메모리를 재할당 하고, 이전 원소를 모두 복사한다면 비효율적이다. 

그래서 원소가 추가될 때 마다 충분한 메모리를 미리 확보시킨다.

컴파일러에 따라 x2, 이전 크기의 1/2 방식으로 미리 할당한다.

vector<int> v; // size:0, cap:0
// v.reserve(8) // reserve를 통해 cap을 미리 8로 예약할 수 있다.

// 2배씩 할당한다.
// 메모리 재할당 -> 복사
v.push_back(1); // size:1, cap:1

// 메모리 재할당 -> 복사
v.push_back(1); // size:2, cap:2

// 메모리 재할당 -> 복사
v.push_back(1); // size:3, cap:4
v.push_back(1); // size:4, cap:4

// 메모리 재할당 -> 복사
v.push_back(1); // size:5, cap:8
v.push_back(1); // size:6, cap:8
v.push_back(1); // size:7, cap:8

resize(), clear() 함수를 통해 size의 크기를 변경할 수도 있다.

vector<int> v(5) = {1,2,3,4,5}; 

v.resize(10); 
// v = 1, 2, 3, 4, 5, 0, 0, 0, 0, 0
// size:10, cap:10

v.resize(5); // v = 1, 2, 3, 4, 5
// size:5, cap:10

v.clear();
// size:0, cap:10

다만 size가 0이 되도 메모리에서는 제거되지 않는다. 메모리에서 제거를 굳이 하고싶다면 swap을 이용하자.

vector<int> v(5);
vector<int>().swap(v); 

// 기본 생성자로 만든 임시 객체(cap:0) v(cap:5)를 스왑!
// 임시 객체 cap:5
// v cap: 5
// 임시객체는 자동으로 제거!

  • 참조

참조에는 front(), back(), at(), [] 연산자가 있다.

front()와 back()은 참조 및 변경이 가능하다.

vector<int> v(5) = {1,2,3,4,5};

cout << v.front() << ", " << v.back() << endl; // 1, 5
v.front() = 10;
v.back() 50;

cout << v.front() << ", " << v.back() << endl; // 10, 50

at()과 [] 연산자는 범위 점검의 유무 차이만 있다.

vector<int> v(5);

cout << v.at(6) << endl; // 에러 X
cout << v[6] << endl; // 에러: out_oif_range

  • 반복자
vector<int> v = {1,2,3,4,5};

vector<int>::iterator iter = v.begin(); // vector<int>::iteartor 반복자 객체 생성

// iter != v.end() iter가 끝을 가리키는지?
// ++iter 반복자를 다음 원소를 가르키도록
for(iter = v.begin(); iter != v.end(); ++iter) 
{
    cout << *iter << endl; // *iter : 반복자가 가리키는 원소 참조
}

iter = v.begin();

// +=, -= 연산자 사용 가능
iter += 2;
cout << *iter << endl; // 3
iter -= 1;
cout << *iter << endl; // 2

// ++ 연산자 가능
cout << *++iter << endl; // 3

// const_iterator가 아니면 수정이 가능하다
*iter = 10; // v = {1, 2, 10, 4, 5}

 

  • 상수
vector<int>::iterator iter; // 이동, 원소의 변경이 가능하다

vector<int>::const_iterator citer; // 이동은 가능하나, 변경은 불가능하다.

const vector<int>::iterator iter_const; // 이동이 불가능하고, 변경은 가능하다.

const vector<int>::const_iterator citer_const; // 이동과 변경 둘다 불가능하다.

 

  • 방향 반복자
vector<int>::iterator iter; // 정방향 반복자
vector<int>::reverse_iterator riter; // 역방향 반복자

for(iter = v.begin(); iter != v.end(); ++iter) // 0 -> v.size()
{
    ...
}

for(iter = v.rbegin(); iter != v.rend(); ++iter) // v.size() -> 0
{
    ...
}

 

  • insert: 중간 삽입 가능, erase: 중간 삭제 가능
vector<int>::iterator iter = v.begin() + 2;
vector<int>::iteartor iter2;

iter2 = v.insert(iter, 100); 
// v = {1, 2, 100, 3, 4, 5}
// *iter2 = 100;

iter2 = v.erase(++iter);
// v = {1, 2, 100, 4, 5}
// *iter = 4;

 

  • 생성
vector<int> v(n); // 1,2,3,4,5

// 순차열 [begin, end)
vector<int> v2(v.begin(), v.end()); // 복사 생성자

// assign
vector<int> v3;
v3.assign(v.begin(), v.end());

  • vector의 주요 특징
  • 임의 접근 반복자(at, [])를 지원하는 배열(원소가 하나의 메모리 블럭에 연속해서 저장) 기반의 컨테이너
  • 메모리 재할당을 줄이기 위해 reserve(), capacity를 배로 증가한다.
  • insert(), erase(), push_back()이 빈번할 경우 다른 컨테이너를 쓰자.
  • 시퀀스 컨테이너(원소가 순서를 유지)로 front()와 back()이 가능하다. 다만 배열 기만이니 push_front()나 pop_front()는 없다.

2. deque 컨테이너

vector와 같이 시퀀스 컨테이너이며 배열 기반의 컨테이너다.

  • 주요 인터페이스와 특징
template<typename T, typename Allocator = allocator<T>> 
class dequeue { ... }

 

  • 생성자
생성자 설명
deque dq 빈 컨테이너
deque dq(n) 기본값으로 초기화된 n개의 원소를 가진다.
deque dq(n,x) x값으로 초기화된 n개의 원소를 가진다.
deque dq(dq2) dq2 컨테이너 복사본(복사생성자)이다
deque dq(b,e) 반복자 구간 [b,e)의 원소를 가진다.

 

  • 멤버 함수 (나머지는 백터와 같다)
할당/추가 설명
push_front(x) 첫 위치에 원소 x를 추가한다.
제거 설명
pop_front() 첫번째 원소를 제거한다.

 

  • 연산자와 멤버형식

백터와 연산자, 멤버형식은 같다.

 

  • 구조

  • 시퀀스 컨테이너 : 순서 유지
  • 배열 기반의 컨테이너 : 임의 접근 반복자(+, -, +=, -=, [])가 가능하다
  • 여러개의 메모리 블록을 하나의 블록처럼 사용 : 복사가 일어나지 않는다.
  • 양끝 삽입/삭제가 가능하다.

원소의 추가 위치에 따라 앞쪽으로 밀지, 뒤로 밀지 정해진다.


3. list 컨테이너

시퀀스 컨테이너로 순서를 유지하나, 노드 기반 컨테이너(at, [] 연산자 사용 X)로 이중 연결 리스트로 구현된다.

  • 인터페이스와 특징
  • 생성자
생성자 설명
list lt 빈 컨테이너
list lt(n) 기본값으로 초기화된 n개의 원소를 갖는다.
list lt(n,x) x값으로 초기화된 n개의 원소를 갖는다.
list lt(lt2) lt2 컨테이너의 복사본이다.
list lt(b,e) [b,e) 값으로 초기화된 원소를 갖는다.

 

  • 멤버 형식 (디큐의 기능 + 추가기능)
제거  
remove(x) 원소 x들을 모두 제거한다
remove_if(pred) pred(단항 연산자)가 참인 경우를 모두 제거한다
특수  
merge(lt2) lt2를 lt로 합병 정렬(오름차순)을 한다.
merge(lt2, pred)  pred를 참으로하는 lt2의 원소들만 lt로 합병 정렬한다.
reverse() 순차열을 뒤집는다.
sort() 오름차순(less)으로 정렬한다
sort(pred) 모든 원소를 pred(이항 연산자) 기준으로 정렬한다.
splice(p, lt2) 반복자 p가 가리키는 위치에 lt2의 모든 원소를 잘라 붙인다
splice(p, lt2, q) 반복자 p가 가리키는 위치에 lt2의 반복자 위치의 원소를 잘라붙인다.
splice(p, lt2, b, e) 반복자 p가 가리키는 위치에 lt2의 [b,e) 원소들을 잘라붙인다.
unique() 인접한 원소의 값이 같다면 중복들을 제거한다
unique(pred) 인접한 원소가 pred(이항 조건자)에 기준에 맞다면 중복을 제거한다.

 

  • 연산자와 템플릿 멤버 형식

기존 시퀀스 컨테이너와 같다


  • 구조와 특징

  • 시퀀스 컨테이너 : 순서 유지
  • 노드 기반의 컨테이너 : at(), [] 연산자 사용 불가능하고 양방향 반복자(++,--)로 탐색한다.
  • 양끝 삽입/삭제가 가능하다
  • 삭제(remove, remove_if)가 추가되었다.
list<int> lt; // 1->2->3->1->4->5->1->1 일 때
lt.remove(1); // 원소 1인 노드들을 모두 제거한다.

for(auto iter = lt.begin(); iter != lt.end(); ++iter)
	cout << *iter << endl; // 2->3->4->5
bool Predicate(int n) // 단항 조건자
{
    return n >= 10 ^^ n <= 30;
}

int main()
{
    list<int> lt; // 1->2->3->4->5->1
    
    lt.remove_if(Predicate); // 10~30 값들 제거
    
    // 4->5
}

※ 리스트의 경우 추가/삭제시 기존 원소들을 당기거나, 밀 필요가 없다.

 

  • 정렬(merge,sort), 이어붙이기(splice), 인접 중복 제거(unique), 원소 뒤집기(reverse)
lt.sort(greater<int>()); // greater<int>() 조건자로 정렬, 내림차순(>)
lt.sort(less<int>()); // less<int>() 조건자로 정렬, 오름차순(<)

struct Greater
{
    bool operator () (int lt, int rt) const
    {
        return lt > rt;
    }
}

lt.sort(Greater()); // 사용자 정의 조건으로 정렬
lt.merge(lt2, less<int>()); // 오름차순으로 lt와 lt2를 합병 정렬한다.
list<int> lt; // 1->2->3->4
list<int> lt2; // 10->20->30

list<int>::iterator iter = lt.begin() + 2; // 3 위치
lt1.splice(iter, lt2); // 1->2->10->20->30->3->4
bool Predicate(int fst, int snd)
{
    return snd-fst <= 0;
}

int main()
{
    list<int> lt; // 1->1->2->3->3->4->5->1
    
    lt.unique(Predicate);
    // 1->2->3->4->5->1
    // 만약 조건이 없는 unique면? 1->2->3->4->5
}

※ 리스트 sort : 알고리즘의 경우 연속 메모리 기반이다보니, 리스트는 딸 ㅗ구현해준 것이다.

 

 

'C++ STL > Part 02 STL 이해' 카테고리의 다른 글

10장 반복자  (0) 2021.06.22
9장 STL 함수 객체  (0) 2021.06.22
8장 알고리즘  (0) 2021.06.22
7장 연관 컨테이너  (0) 2021.06.22
5장 STL 소개  (0) 2021.06.22

댓글