STL vector
vector는 배열과 같이, 같은 타입의 데이터를 저장하기 위해 쓰이는 STL( Standard Template Library ) 객체입니다.
하지만, 배열과 달리 vector의 크기는 필요에 따라 조절할 수 있습니다.
이 vector 객체를 사용하려면 다음의 헤더를 선언해야 합니다.
#include <vector>
vector의 선언
vector의 선언은 다음과 같습니다.
std::vector<T> vector_name;
여기서 T는 vector의 데이터 타입을 나타냅니다.
vector 선언 시 주목할 점은, vector의 크기를 명시할 필요가 없다는 것입니다.
vector의 크기는 데이터를 추가하거나 삭제할 때, 동적으로 변경됩니다.
vector의 초기화
vector를 초기화하는 방법은 여러 가지가 있습니다.
1. 초기화 리스트를 사용하는 방법입니다.
#include <iostream>
#include <vector>
using namespace std;
vector<int> int_vec = { 1, 2, 3, 4, 5 }; // 초기화 리스트
for( int data : int_vec ){
cout << data << " ";
}
2. 유니폼 초기화( uniform initialization )를 사용하는 방법입니다.
vector<int> int_vec { 1, 2, 3, 4, 5 }; // uniform 초기화
3. 직접 크기와 값을 설정할 수도 있습니다.
vector<int> int_vec (5, 10); // 5개의 원소를 값 10으로 초기화합니다.
vector<int> int_vec = { 10, 10, 10, 10, 10 }; // 위의 줄과 동일합니다.
4, 다른 vector를 복사하여 초기화할 수도 있습니다.
vector<int> int_vec = { 1, 2, 3, 4, 5 };
vector<int> copy_vec(int_vec);
기본적인 Vector의 기능들
vector는 매우 많은 기능을 가진 객체입니다.
이 중에서, 공통적으로 많이 쓰이는 기능을 정리해서 설명하겠습니다.
원소의 개수 알아내기
size
vector가 저장하고 있는 개수를 반환합니다.
empty
vector가 가지고 있는 원소의 개수가 0이면 true, 아니면 false를 반환합니다.
int main(){
vector<int> int_vec = { 1, 2, 3, 4, 5 };
cout << int_vec.size() << endl;
cout << "is empty: " << int_vec.empty();
return 0;
}
▼출력
5
is empty: 0
capacity
원소를 저장할 수 있는 크기를 반환합니다.
예를 들어, 3개의 원소를 갖고 있고, capacity 함수가 5를 반환했다면
vector는 재할당 없이 2개의 원소를 추가로 저장할 수 있습니다.
원소를 저장할 수 있는 공간이 모두 차면 재할당 자동으로 일어나는데, 재할당이 일어나면 기존의 데이터를 복사하는 과정이 필요합니다.
따라서, 너무 많은 재할당 일어나는 상황에서는 성능이 떨어지게 됩니다.
reserve
원소를 저장할 수 있는 크기를 예약합니다.
너무 큰 공간을 예약하면, 재할당 과정은 없겠지만, 메모리 낭비가 심할 수 있습니다.
그러나, 필요한 메모리의 양을 추정할 수 있다면 최상의 성능을 낼 수 있는 기능이기도 합니다.
int main(){
vector<int> int_vec = { 1, 2, 3, 4, 5 };
int_vec.push_back(6);
cout << "size: " << int_vec.size() << endl;
cout << "capacity: " << int_vec.capacity() << endl;
int_vec.reserve(20);
cout << "after reserve, capacity: " << int_vec.capacity() << endl;
return 0;
}
▼출력
size: 6
capacity: 10
after reserve, capacity: 20
이 함수가 벡터의 용량을 조정하는 것이라면, 이와 달리 벡터의 원소 개수를 조정하는 함수인 resize도 있습니다.
이 reserve와 resize 함수의 차이점에 대한 내용은 여기에서 볼 수 있습니다.
원소 추가하기
push_back
push_back 함수는 새로운 원소를 vector의 뒤 쪽에 추가합니다.
int main(){
vector<int> int_vec = { 1, 2, 3, 4, 5 };
int_vec.push_back(6);
int_vec.push_back(7);
for( int data : int_vec ){
cout << data << " ";
}
return 0;
}
▼출력
1 2 3 4 5 6 7
insert
insert 함수는 원하는 위치에 새로운 원소를 추가합니다.
추가하려는 위치를 가리키기 위해서 iterator를 사용해야 되고, 성공적으로 추가하게 되면 추가된 원소의 위치를 나타내는 iterator를 반환합니다.
int main(){
vector<int> int_vec = { 1, 2, 3, 4, 5 };
int_vec.insert( int_vec.begin(), 6); // 첫 번째 위치에 추가
int_vec.insert( int_vec.end(), 7 ); // 맨 마지막에 추가
for( int data : int_vec ){
cout << data << " ";
}
return 0;
}
▼출력
6 1 2 3 4 5 7
emplace_back
emplace_back 함수는 새로운 원소를 vector 내에서 직접 생성하고, 원소 배열의 뒤 쪽에 추가합니다.
int main(){
using point = pair<int, int>;
vector<point> point_vec;
point_vec.emplace_back( 1, 1); // 벡터 내부에 point 직접 생성
point pt2 = make_pair( 2, 2);
point_vec.emplace_back( pt2);
for( auto& x : point_vec){
cout << x.first << ", " << x.second << endl;
}
}
▼출력
1, 1
2, 2
이 함수에 관해선 여기에서 좀 더 자세히 볼 수 있습니다.
원소에 접근하기
at
at 함수는 vector의 원소를 index를 사용해 접근할 수 있도록 합니다.
int main(){
vector<int> int_vec = { 1, 2, 3, 4, 5 };
int val = int_vec.at(2); // 세 번째 원소에 접근
int val2 = int_vec[2]; // 연산자를 통해 접근
return 0;
}
at 함수 대신 연산자 [ ]를 통해서 원소에 접근하는 방법도 있습니다.
그러나, index가 잘못되는 경우 연산자 [ ]는 잘못된 값을 반환하지만, at 함수는 예외(exception)를 발생시킵니다.
int main(){
vector<int> int_vec = { 1, 2, 3, 4, 5 };
int val = int_vec.at(7); // 존재하지 않는 원소에 접근. 예외 발생
int val2 = int_vec[7]; // 연산자를 통해 접근. 잘못된 값을 반환
return 0;
}
원소 변경하기
at
원소의 값을 바꾸는 데도, at 함수를 사용할 수 있습니다.
int main(){
vector<int> int_vec = { 1, 2, 3, 4, 5 };
int_vec.at(2) = 6; // at 함수를 통한 변경
int& ref = int_vec.at(3);
ref = 7;
int_vec[4] = 7; // 연산자를 통한 변경
for( int val : int_vec){
cout << val << " ";
}
return 0;
}
▼출력
1 2 6 7 7
at 함수는 보관하고 있는 데이터의 참조(reference)를 반환하기 때문에, 위와 같은 대입 방식이 가능합니다.
그리고, [ ] 연산자를 통해서도 값을 변경할 수 있습니다.
int main(){
vector<int> int_vec = { 1, 2, 3, 4, 5 };
int_vec.at(7) = 6; // 존재하지 않는 원소의 값 변경. 예외 발생
int_vec[7] = 100; // 연산자를 통해 접근. 경고를 줄 수 없습니다.
return 0;
}
그러나, at 함수는 존재하지 않는 원소의 값을 변경하면, 예외를 발생시켜 경고를 받을 수 있지만,
연산자 [ ]를 통한 변경의 경우 오류를 찾기 힘들 수도 있습니다.
원소 삭제하기
pop_back
vector에서 마지막 원소를 삭제합니다.
int main(){
vector<int> int_vec;
int_vec.push_back(3); // 마지막에 원소 추가
int_vec.pop_back(); // 마지막의 원소 삭제
return 0;
}
clear
vector에 있는 모든 원소를 삭제합니다.
int main(){
vector<int> int_vec = { 1, 2, 3, 4, 5 };
int_vec.clear(); // 모든 데이터 삭제
cout << int_vec.size(); // 0이 반환됩니다.
return 0;
}
Vector의 반복자( Iterator )
vector 반복자는 vector 내의 원소들에 접근하고, 원소들 간의 이동을 위해서 사용됩니다.
vector의 반복자는 임의 접근 반복자( random access iterator )입니다.
반복자( iterator )의 전반적인 내용은 여기에 정리해 두었습니다.
vector 반복자는 다음과 같이 선언합니다.
vector<T>::iterator iter_name;
여기서 T는 vector의 데이터 타입입니다.
vector의 반복자 초기화
vector의 초기화는 다음의 함수들로 수행할 수 있습니다.
begin
이 함수는 vector의 첫 번째 원소를 가리키는 iterator를 반환합니다.
vector<int> int_vec = { 1, 2, 3, 4, 5 };
vector<int>::iterator iter; // int타입 vector의 반복자 선언
iter = int_vec.begin();
end
이 함수는 vector의 마지막 원소 다음을 가리키는 iterator를 반환합니다.
마지막 원소를 가리키는 iterator는 다음과 같이 구할 수 있습니다.
vector<int> int_vec = { 1, 2, 3, 4, 5 };
vector<int>::iterator iter;
iter = int_vec.end() - 1;
int val = *iter; // 마지막 원소 5
만일, vector의 원소가 하나도 없다면 begin과 end가 같은 값을 반환합니다.
#include <iostream>
#include <vector>
using namespace std;
int main(){
vector<int> int_vec;
if ( int_vec.begin() == int_vec.end()){
cout << "vector size is zero\n";
}
return 0;
}
▼출력
vector size is zero
vector의 모든 원소를 순환하고자 하면 다음과 같이 할 수 있습니다.
int main(){
vector<int> int_vec = { 1, 2, 3, 4, 5 };
vector<int>::iterator iter;
for(iter = int_vec.begin(); iter != int_vec.end(); iter++){
cout << *iter << " ";
}
return 0;
}
▼출력
1 2 3 4 5
이 글과 관련있는 글들
함수에서 std::vector와 같은 객체를 반환하는 방법
'C, C++ > 자료구조' 카테고리의 다른 글
[C++] STL map 사용법 (0) | 2024.06.27 |
---|---|
[C++] 덱 ( deque ) 사용법 (0) | 2024.06.12 |
[C++] STL list 사용법 (0) | 2024.06.11 |
[C++] 우선 순위 큐 ( Priority Queue ) 사용법 (0) | 2024.06.10 |
[C++] 반복자( Iterator )에 대한 설명과 종류 (0) | 2024.06.06 |