본문 바로가기
프로그래밍/C++

C++ ] vector 사용법

by eteo 2023. 3. 31.

 

 

vector 사용법

 

생성자

 

1. 기본 생성자: 요소의 타입을 지정하지 않으면 빈 벡터가 생성

std::vector<int> vec; // 빈 벡터 생성

 

2. 초기화 리스트: 중괄호를 이용해 초기 값을 지정

std::vector<int> vec = {1, 2, 3}; // 값이 {1, 2, 3}인 벡터 생성

 

3. 크기 지정: 생성 시, 초기값이 지정된 요소의 개수를 지정

std::vector<int> vec(5); // 크기가 5이고 모든 값이 0인 벡터 생성
std::vector<int> vec(5, 2); // 크기가 5이고 모든 값이 2인 벡터 생성

 

4. 복사 생성자: 다른 벡터를 복사하여 생성

std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2(vec1); // vec1을 복사한 벡터 생성

 

5. 범위 기반 생성자: 다른 컨테이너에서 값을 가져와 벡터를 생성

std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2(vec1.begin(), vec1.end()); // vec1의 범위를 가져와 벡터 생성
std::vector<int> vec2(vec1.begin(), vec1.begin() + 2); // vec2 = {1, 2}, 두번째 인자에는 마지막 요소의 다음 위치를 가리키는 iterator가 온다.

 

 

 

 

 

 

 

 

 

 

메소드

 

1. push_back() : 벡터의 끝에 요소를 추가

std::vector<int> vec;
vec.push_back(1); // vec = {1}
vec.push_back(2); // vec = {1, 2}
vec.push_back(3); // vec = {1, 2, 3}

 

2. pop_back() : 벡터의 끝에 있는 요소를 제거

std::vector<int> vec = {1, 2, 3};
vec.pop_back(); // vec = {1, 2}

 

3. size() : 벡터의 크기를 반환

std::vector<int> vec = {1, 2, 3};
std::cout << vec.size(); // 3 출력

 

4. empty() : 벡터가 비어있는지 여부를 반환

std::vector<int> vec = {1, 2, 3};
if (vec.empty()) {
    std::cout << "벡터가 비어있습니다.";
}

 

5. resize() : 벡터의 크기를 변경

std::vector<int> vec = {1, 2, 3};
vec.resize(5); // vec = {1, 2, 3, 0, 0}
vec.resize(2); // vec = {1, 2}

 

6. insert() : 벡터의 특정 위치에 요소를 삽입

std::vector<int> vec = {1, 2, 3};
vec.insert(vec.begin()+1, 4); // vec = {1, 4, 2, 3}

 

7. erase() : 벡터의 특정 위치에 있는 요소를 제거

std::vector<int> vec = {1, 2, 3};
vec.erase(vec.begin()+1); // vec = {1, 3}
vec.erase(vec.begin()+1, vec.end()); // vec = {1}

 

8. front() : 벡터의 첫 번째 요소를 반환

std::vector<int> vec = {1, 2, 3};
std::cout << vec.front(); // 1 출력

 

9. back() : 벡터의 마지막 요소를 반환

std::vector<int> vec = {1, 2, 3};
std::cout << vec.back(); // 3 출력

 

10.  begin() : 벡터의 첫번째 요소를 가리키는 iterator를 반환한다.

 

11.  end() : 벡터의 마지막 요소 다음 위치를 가리키는 iterator를 반환한다.

📍 예를 들어, std::vector<int> vec = {1, 2, 3};에서 vec.end()는 실제로 vec의 마지막 요소 3이 아니라 그 다음 위치를 가리키게 된다. 그리고 알고리즘 라이브러리에서 firstIter, lastIter를 인자로 받는 함수들도 lastIter는 마지막 요소 다음 위치를 의미한다.

 

12. clear() : 벡터의 모든 요소를 제거

 

std::vector<int> vec = {1, 2, 3};
vec.clear(); // vec = {}

 

 

13. data() : 벡터의 내부 데이터 배열에 대한 포인터를 반환

벡터의 첫 번째 요소를 가리키는 포인터를 제공하여 C스타일 접근이 가능하게 해준다. 벡터에 저장된 요소의 타입에 따라서 T* 포인터 타입이 반환된다.

 

std::vector<int> vec = {1, 2, 3, 4, 5};
int* ptr = vec.data();

std::cout << ptr[0] << std::endl; // 1 출력
std::cout << ptr[1] << std::endl; // 2 출력

 

 

14. emplace_back : 벡터의 끝에 요소를 추가하는 함수로 push_back 함수와 비슷하다.

push_back 함수는 요소를 추가하기 위해 임시객체를 만들어 생성, 이동, 소멸하는 과정이 포함된다면, emplace_back 함수는 객체 생성에 필요한 인자를 받아서 함수 내에서 객체를 직접 "in-place"로 생성하므로, 별도의 객체 복사나 이동이 필요 없게 해준다.

 

📍  특히 생성 비용이 큰 객체들을 벡터에 추가할 때 성능 향상을 가져올 수 있지만 암시적 형변환으로 인한 예상치 못한 문제가 발생할 수 있으므로 주의해서 사용해야 한다. (관련글: https://openmynotepad.tistory.com/10)

std::vector<int> vec;
vec.emplace_back(1);
struct MyStruct {
    MyStruct(int x, double y) : x(x), y(y) {}
    int x;
    double y;
};

std::vector<MyStruct> myVec;
myVec.emplace_back(1, 3.14); // MyStruct 객체를 인자 1, 3.14로 직접 생성하여 추가
// push_back을 사용하는 경우 아래와 같이 요소를 추가해야 한다.
myVec.push_back(MyStruct(1, 3.14));

 

 

 

❗ vector::clear()를 호출하면 벡터의 size는 0이 되지만, 용량(capacity)은 그대로 유지된다.

만약 벡터가 차지하는 메모리를 완전히 해제하고 싶다면 vector::swap()을 사용하여 빈 벡터와 swap하면 된다. 이렇게하면 기존 벡터가 차지했던 메모리는 모두 해제된다.

 

vector<int> vec {1, 2, 3};
vec.clear(); // size = 0, capacity = 3

vector<int>().swap(vec); // vec의 메모리를 해제하고, 빈 벡터와 swap
// vec는 더 이상 메모리를 차지하지 않음

 

 

 

 

 

 

 

 

 

인덱스를 통해 원소에 접근하기

 

1. [] 사용

2. at() 메소드 사용

 

일반적으로 [] 연산자를 이용해서 벡터의 원소에 접근할 수 있지만, at 메소드는 범위를 체크하여 안전하게 원소에 접근할 수 있다는 차이가 있다.

 

 

 

 

 

 

Iterator

 

Iterator(반복자)는 컨테이너에 저장된 요소에 접근하는 방법 중 하나로, 포인터와 비슷한 개념이다. Iterator는 범용적으로 작성된 코드를 작성할 수 있도록 해주며, STL(Standard Template Library)의 알고리즘 함수들과 함께 사용된다. Iterator는 포인터와 유사하지만 연산자 오버로딩을 통해 더 다양한 연산을 지원한다. 

 

1. 벡터에 대한 Iterator 생성

vector<int> my_vec {1, 2, 3, 4, 5};

// 벡터의 시작과 끝 iterator를 가져옵니다.
auto it_start = my_vec.begin();  // 시작 iterator
auto it_end = my_vec.end();      // 끝 iterator

// 혹은
vector<int>::iterator it;
it = my_vec.begin();

 

 

2. Iterator를 사용하여 요소에 접근

vector<int> my_vec {1, 2, 3, 4, 5};

// 벡터의 시작 iterator를 가져옵니다.
auto it = my_vec.begin();

// iterator를 사용하여 요소에 접근합니다.
cout << *it << endl;  // 출력: 1

// iterator를 이동하여 다음 요소에 접근합니다.
++it;
cout << *it << endl;  // 출력: 2

 

 

3. Iterator 사용하여 반복

vector<int> my_vec {1, 2, 3, 4, 5};

// 벡터의 끝에 새로운 요소를 추가합니다.
my_vec.push_back(6);

// 벡터의 모든 요소를 출력합니다.
for (auto it = my_vec.begin(); it != my_vec.end(); ++it) {
    cout << *it << " ";
}
// 출력: 1 2 3 4 5 6

 

 

4. Iterator를 사용하는 알고리즘 함수

#include <iostream>
#include <vector>
#include <numeric> // std::accumulate를 사용하기 위한 헤더
#include <algorithm> // std::sort를 사용하기 위한 헤더

using namespace std;

int main() {
    vector<int> vec = {1, 2, 3, 4, 5};
    int sum = accumulate(vec.begin(), vec.end(), 0); // 모든 원소를 더한 값을 반환
    cout << "Sum of all elements: " << sum << endl;
    
    vector<int> vec2 = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
    sort(vec2.begin(), vec2.end());  // 벡터의 모든 원소를 오름차순으로 정렬
	// ...
    return 0;
}

 

 

 

 

 

 

범위기반 for문

 

범위 기반 for문을 사용하면 컨테이너를 간편하게 순회할 수 있다. for 키워드 뒤에 괄호 안에 반복 변수를 선언하고, 그 다음에 : 연산자와 컨테이너를 넣어주면 된다. 이때 반복 변수는 컨테이너의 요소 타입과 일치해야 합니다. 단, 반복 변수가 값 타입이면 복사가 일어나지만 참조 타입을 사용하여 원본 벡터를 조작하거나 메모리를 아낄 수 있다.

 

std::vector<int> vec = {1, 2, 3};

for (int& x : vec) {
    x *= 2;
}

for (int x : vec) {
    std::cout << x << " ";
}
// 출력 결과: 2 4 6

 

 

 

 

 

vector에서 특정 값 찾기

 

 

<algorithm> 헤더에 정의된 std::find() 함수는 시퀀셜 컨테이너(배열, 벡터, 리스트 등)의 시작과 끝 이터레이터를 인자로 받아, 지정된 값과 같은 첫 번째 요소를 찾는 함수로 이 함수를 사용해 벡터에서 원하는 값의 위치를 알아낼 수 있다.

 

find() 함수는 만약 값이 컨테이너에 존재하지 않는다면 끝 iterator를 반환한다. 끝이 아닌 iterator를 반환한 경우 해당 값을 찾은 경우이며 해당 iterator 를 사용해 요소에 직접 접근하거나 혹은 시작 iterator를 빼서 해당 값이 위치한 인덱스를 알 수 있다.

 

find(start_iterator, end_iterator, value_to_find)

 

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> data = {1, 2, 3, 4, 5};
    
    auto it = std::find(data.begin(), data.end(), 3);

    if (it != data.end()) {
        std::cout << "Found at : " << it - data.begin() << std::endl;
    } else {
        std::cout << "Not found." << std::endl;
    }

    return 0;
}