Posted Updated

STL

C++ STL(Standard Template Library)은 자료구조와 알고리즘을 C++의 템플릿 형태로 제공하는 표준 라이브러리로, 효율적이고 재사용 가능한 코드를 작성할 수 있도록 돕는다. STL은 크게 컨테이너(Container), 반복자(Iterator), 알고리즘(Algorithm)으로 구성되어 있으며, 이 중에서 컨테이너는 데이터를 저장하고 관리하는 역할을 한다.

vector

C++ STL의 vector는 동적 배열(dynamic array)을 구현한 컨테이너로, 가장 널리 사용되는 STL 컨테이너 중 하나이다. std::vector는 배열처럼 연속된 메모리 공간에 데이터를 저장하며, 크기가 자동으로 조절되는 특징을 가지고 있다.

기본 정의

#include <vector>

std::vector<int> v; // int형을 저장하는 벡터

주요 특징

특징 설명
동적 배열 크기를 동적으로 조절 (push_back, resize 등)
임의 접근 지원 v[i], v.at(i) 사용 가능 (random access O(1))
연속된 메모리 구조 배열처럼 메모리에 연속적으로 저장되어 캐시 효율이 좋음
자동 메모리 관리 내부적으로 new, delete 수행, 직접 메모리 관리할 필요 없음
타입 안전성 템플릿 기반으로 타입에 따라 동작

주요 멤버 함수

함수명 설명
push_back(value) 맨 뒤에 value 삽입
pop_back() 맨 뒤 요소 삭제
size() 현재 벡터의 원소 개수 반환
capacity() 현재 저장 가능한 최대 원소 개수 반환
resize(n) 크기를 n으로 조정
clear() 모든 원소 제거
empty() 비어 있으면 true 반환
at(i) 인덱스 i번째 원소 반환 (범위 검사 O)
operator[] 인덱스 접근 (범위 검사 X)
begin(), end() 반복자 반환
insert() 중간 삽입
erase() 중간 삭제
swap() 다른 벡터와 교환
shrink_to_fit() capacity를 size에 맞게 줄임

예시 코드

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

int main() {
    vector<int> v;

    // push_back
    v.push_back(10);
    v.push_back(20);
    v.push_back(30);

    // size, capacity
    cout << "size: " << v.size() << ", capacity: " << v.capacity() << "\n";

    // operator[], at()
    cout << "v[1] = " << v[1] << ", v.at(1) = " << v.at(1) << "\n";

    // resize
    v.resize(5); // 뒤에 0으로 채워짐
    cout << "After resize(5): ";
    for (int n : v) {
        cout << n << " ";
    }
    cout << "\n";

    // insert
    v.insert(v.begin() + 2, 99); // 3번째 위치에 99 삽입
    cout << "After insert(2, 99): ";
    for (int n : v) {
        cout << n << " ";
    }
    cout << "\n";

    // erase
    v.erase(v.begin() + 3); // 4번째 요소 제거
    cout << "After erase(3): ";
    for (int n : v) {
        cout << n << " ";
    }
    cout << "\n";

    // pop_back
    v.pop_back(); // 맨 뒤 요소 제거
    cout << "After pop_back(): ";
    for (int n : v) {
        cout << n << " ";
    }
    cout << "\n";

    // empty
    cout << "Is vector empty? " << (v.empty() ? "Yes" : "No") << "\n";

    // swap
    vector<int> other = {100, 200};
    v.swap(other);
    cout << "After swap with {100, 200}: ";
    for (int n : v) {
        cout << n << " ";
    }
    cout << "\n";

    // shrink_to_fit
    v.resize(1); // size 줄이기
    v.shrink_to_fit(); // capacity 줄이기
    cout << "After resize(1) and shrink_to_fit(): size = " << v.size() << ", capacity = " << v.capacity() << "\n";

    // clear
    v.clear();
    cout << "After clear(): size = " << v.size() << ", empty = " << (v.empty() ? "Yes" : "No") << "\n";

    return 0;
}

🔽 출력 :

size: 3, capacity: 4
v[1] = 20, v.at(1) = 20
After resize(5): 10 20 30 0 0
After insert(2, 99): 10 20 99 30 0 0
After erase(3): 10 20 99 0 0
After pop_back(): 10 20 99 0
Is vector empty? No
After swap with {100, 200}: 100 200
After resize(1) and shrink_to_fit(): size = 1, capacity = 1
After clear(): size = 0, empty = Yes

내부 동작: 메모리 재할당

capacity()는 일반적으로 size()보다 크다. push_back() 시 capacity()를 초과하면 2배로 크기를 늘린다. 이때 기존 데이터를 새로운 메모리로 복사해야 하므로 O(n)의 시간이 소요된다.

따라서 많은 삽입이 예상되면 reserve() 를 통해 미리 메모리를 확보할 수 있다.

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

int main() {
    int N;
    cin >> N;

    vector<int> v;

    for (int i = 0; i < N; i++) {
        int number;
        cin >> number;
        v.push_back(number);
    }
}

보통 입력 받을 때, 알아서 크기가 늘어나므로 이런 식으로 코드를 많이 짠다. 이때,

vector<int> v;
v.reserve(10000);

대충 N의 크기를 예측해 reserve() 함수를 미리 사용하면 성능 향상을 도모해볼 수도 있다.

주의할 점으로는 중간 삽입/삭제가 O(n)이라는 점이다. 중간에 공간이 필요하거나 비게 되면 그만큼 뒤에 있는 요소들을 옮겨야 하기 때문에 다른 자료구조가 더 적합할 수 있다. (ex. list)

요약

장점 단점
임의 접근 속도 빠름 (O(1)) 중간 삽입/삭제 느림 (O(n))
메모리 연속적, 캐시 친화적 capacity 초과 시 재할당 비용 발생

Leave a comment