만약에 입력으로 n을 입력받아 크기가 n인 배열을 만들고 싶다면 어떻게 해야 할까?
int n;
cin >> n;
int arr[n];
이렇게 하면 될까?
이렇게 선언하면 컴파일 에러가 나는 것을 볼 수 있다.
배열의 크기를 나타내는 [] 안에는 const로 선언된 상수 변수 또는 리터럴 상수만 들어갈 수 있다.
따라서 이 경우 포인터를 이용한 동적할당을 해야 한다.
이렇게 동적 할당을 해주면 에러가 안 나고 크기가 n인 int 배열이 잘 선언되었다.
하지만 배열의 크기를 미리 알지 못 할수도 있다.
입력이 계속 들어오고 입력이 0일 경우 입력을 그만 받는 프로그램이 있을 수도 있다.
이 경우 배열의 크기를 얼마로 잡아야 할까?
물론 int arr[100000]; 이렇게 크게 잡아놓고 입력을 받을 수도 있지만 메모리 공간을 너무 낭비한다는 단점이 있고
입력으로 들어온 수의 개수를 초과하는 인덱스에 접근해도 IndexOutOfRange예외가 발생하지 않기 때문에 쓰레기값을 가지고 엉뚱한 계산을 할 수도 있다.
배열의 이러한 한계를 극복한 것이 vector 클래스이다. 벡터는 사용하다가 크기가 부족해도 크기를 늘릴 수도 있고 초기에 크기를 선언하지 않아도 되기 때문에 배열보다 훨씬 유연하다. 그리고 벡터는 배열과 다르게 클래스이기 때문에 유용한 함수들도 들어있어서 더욱 편하다.
http://www.cplusplus.com/reference/vector/vector/
vector - C++ Reference
difference_typea signed integral type, identical to: iterator_traits ::difference_type usually the same as ptrdiff_t
www.cplusplus.com
벡터의 선언 방법은 다음과 같다.
#include <vector>
일단 <vector> 헤더파일을 포함시켜주고
vector<int> v;
이렇게 선언한다. 그러면 v라는 빈 벡터가 생성된다.
벡터는 vector<T> 이렇게 만들어진 템플릿 클래스이기 때문에 벡터에 들어갈 타입을 <>이 사에에 적어줘야 한다.
이렇게 선언한 벡터 v에 1이라는 정수를 집어넣고 싶다면?
v.push_back(1);
이렇게 하면 된다.
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v;
v.push_back(1);
v.push_back(7);
v.push_back(15);
for (int i = 0; i < v.size(); i++) {
cout << v[i] << endl;
}
return 0;
}
이 코드의 실행 결과는 다음과 같다.
벡터의 모든 원소를 for문을 돌리면서 출력하는데 v.size()를 사용했다.
v.size()를 하면 벡터에 있는 원소의 개수를 반환한다. 지금 이 코드의 경우 v.size()를 하면 3을 반환한다.
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v;
v.push_back(1);
v.push_back(7);
v.push_back(15);
cout << "벡터의 크기: " << v.size() << endl;
return 0;
}
위 코드의 실행 결과
지금 우리가 벡터를 사용한 방법은 빈 벡터를 선언하고 push_back() 함수를 사용하여 값을 넣어주었다.
하지만 벡터의 크기를 처음부터 잡고 싶을 때엔 어떻게 해야 할까?
vector<int> v(3);
이렇게 선언하면 크기가 3인 벡터가 생성된다.
그럼 이렇게 크기가 3인 벡터를 선언하고 아까와 같이 push_back 함수를 통해 1, 7, 15를 벡터에 넣으면 어떻게 될까?
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v(3);
v.push_back(1);
v.push_back(7);
v.push_back(15);
for (int i = 0; i < v.size(); i++) {
cout << v[i] << endl;
}
return 0;
}
위 코드의 실행 결과
크기가 3인 벡터를 선언하고 push_back()으로 원소를 넣으면 이렇게 뒤에 원소가 추가되는 것을 볼 수 있다.
즉 push_back() 함수를 호출하면 반드시 벡터의 크기가 늘어난다는 것을 알 수 있다.
그리고 벡터의 크기를 정해서 선언하면 초기값 0으로 채워진 벡터가 생성된다는 것도 알 수 있다.
따라서 이렇게 일정 크기의 벡터를 선언하면 push_back()이 아니라
v[0] = 1;
v[1] = 7;
v[2] = 15;
이렇게 인덱스로 접근할 수 있다.
그러면 초기값을 0이 아니라 5로 주고 싶으면 어떻게 해야 할까?
vector<int> v(3, 5); //모든 원소가 5인 크기가 3인 벡터 생성.
이렇게 선언하면 초기값 5로 채워진 크기가 3인 벡터가 생성된다.
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v(3, 5);
for (int i = 0; i < v.size(); i++) {
cout << v[i] << endl;
}
return 0;
}
위 코드의 실행 결과
이 코드를 실행하면 이렇게 5로 초기화되어 있는 것을 볼 수 있다.
또 내가 원하는 초기값이 다 다르다면 이렇게도 가능하다.
vector<int> v = {1, 7, 15};
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = { 1, 7, 15 };
cout << "벡터의 크기: " << v.size() << endl;
for (int i = 0; i < v.size(); i++) {
cout << v[i] << endl;
}
return 0;
}
위 코드의 실행 결과
벡터 클래스의 대표적인 생성자를 다시 살펴보자면
+vector()
T 타입의 빈 벡터 생성
+vector(size: int)
숫자의 경우 0으로, bool의 경우 false로 초기화된 size 크기의 벡터 생성
+vector(size: int, defaultValue: T)
defaultValue로 초기화된 size 크기의 벡터 생성
자주 쓰이는 대표적인 생성자는 이렇게 3개가 있다.
그러면 이제 vector 클래스의 유용한 함수들을 살펴보자.
+push_back(element: T): void
벡터에 element를 원소로 추가
+pop_back(): void
벡터의 마지막 원소를 삭제
+size(): unsigned int const
벡터의 크기(원소 개수) 반환
+at(index: int): T const
index 위치에 있는 원소 반환. v[index]로 접근하는 것과 같다.
+empty(): bool const
벡터의 크기가 0인지 아닌지 반환
+clear(): void
벡터의 모든 원소를 삭제
+swap(v2: vector): void
벡터의 내용을 다른 벡터 v2와 교환. 같은 타입의 벡터만 가능
+erase(iterator position): iterator
position 위치에 있는 원소를 삭제
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v;
cout << "벡터의 크기: " << v.size() << endl;
cout << "벡터가 비어있나요? " << (v.empty() ? "true" : "false") << endl;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
cout << "벡터가 비어있나요? " << (v.empty() ? "true" : "false") << endl;
cout << "벡터의 크기: " << v.size() << endl;
cout << "0번째 원소: " << v[0] << endl; //이렇게 []로도 접근 가능하고
cout << "3번째 원소: " << v.at(3) << endl; //이렇게 at() 함수로도 인덱스에 접근 가능
for (int i = 0; i < v.size(); i++) {
cout << v[i] << " ";
}
cout << endl;
v.pop_back();
for (int i = 0; i < v.size(); i++) {
cout << v[i] << " "; //마지막 원소였던 5가 삭제된 것을 볼 수 있음
}
cout << endl;
v.clear(); //벡터의 모든 원소 삭제
cout << "벡터의 크기: " << v.size() << endl;
cout << "벡터가 비어있나요? " << (v.empty() ? "true" : "false") << endl;
cout << endl;
return 0;
}
위 코드의 실행 결과
이 코드와 실행 결과만 보면 함수 사용법은 쉽게 알 수 있으리라 생각된다.
erase 함수 사용법은 조금 어려울 수도 있기 때문에 따로 빼서 설명하자면 예를 들어 i번째 원소를 벡터에서 삭제하고 싶다면 다음과 같이 쓰면 된다.
v.erase(v.begin() + i);
이렇게 하면 된다. erase 함수의 인자는 인덱스가 아니라 주소이기 때문에 저렇게 써야 한다. v.begin()은 벡터 v의 시작 주소이다.
전체 코드로 실습을 해보자면
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v(10);
for (int i = 0; i < 10; i++) {
v[i] = i;
}
cout << "지우기 전: ";
for (int i = 0; i < v.size(); i++) {
cout << v[i] << " ";
}
cout << endl;
v.erase(v.begin() + 5);
cout << "5번 인덱스 지운 후: ";
for (int i = 0; i < v.size(); i++) {
cout << v[i] << " ";
}
cout << endl;
return 0;
}
위 코드의 실행 결과
v.erase(v.begin() + 5);로 5번 인덱스에 있는 원소가 삭제된 것을 볼 수 있다.
그러면 이차원 배열이 필요하면 어떻게 해야 할까?
벡터의 벡터로 이차원 배열을 구현할 수 있다. 5행 3열짜리 이차원 배열을 선언하고 싶다면 다음과 같이 하면 된다.
vector<vector<int>> v(5);
for (int i = 0; i < 5; i++) {
v[i] = vector<int>(3);
}
코드를 보면 크기가 5인 벡터를 선언하는데 벡터의 원소로 벡터를 가지는 벡터이다.
즉, 빈 벡터 5개를 원소로 가지는 벡터이다.
그러고 나서 for문을 돌면서 각각의 빈 벡터를 크기가 3인 벡터로 할당해준다.
그러면 5x3짜리 이차원 배열과 같은 벡터가 만들어진다.
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<vector<int>> v(5);
for (int i = 0; i < 5; i++) {
v[i] = vector<int>(3);
}
for (int i = 0; i < v.size(); i++) {
for (int j = 0; j < v[i].size(); j++) {
cout << v[i][j] << " ";
}
cout << endl;
}
return 0;
}
위 코드의 실행 결과
이 코드를 줄여서 한 줄에 처리하고 싶다면
vector<vector<int>> w(5, vector<int>(3));
이렇게 하면 된다.