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;
}
'프로그래밍 > C++' 카테고리의 다른 글
C++ ] accumulate 함수 사용시 주의사항 (0) | 2023.03.31 |
---|---|
C++ ] 2차원 vector 사용법 (0) | 2023.03.31 |
C++ ] STL 과 컨테이너 (0) | 2023.03.25 |
범위기반 for문, 문자열에서 문자 반복 (0) | 2023.03.25 |
C 와 C++ 으로 10진수를 2진수로 변환하여 출력하기 (0) | 2022.09.01 |