remove
함수는 algorithm
헤더를 선언해주어야 한다.
erase
함수는 vector
의 함수들 중 하나이기 때문에, vector
헤더를 선언해주어야 한다.
remove(first, last, 삭제하려는 값); // 범위는 [first,last)
remove
함수는 특정 범위를 탐색하여 그 범위에 있는 특정 값을 삭제할 수 있다.
// vector의 이름이 v라고 가정
// 1
v.erase(first, last); // 범위는 [first,last)
// 2
v.erase(삭제하려는 값의 위치);
erase
함수는 특정 범위에 속하는 모든 값들을 삭제하거나(해당 범위에서의 특정 값은 삭제 불가능), 특정 값의 위치를 보내면 해당 값을 삭제할 수 있다
둘 다 first
와 last
에 포함되는 범위가 [first, last)
이다. last
는 범위에 포함되지 않는다는 것을 기억하자!
그리고 두 함수 모두 맨 끝 범위에 위치한 값은 삭제할 수 없다!
ex) 1 2 3 4 5 가 있을 때, 범위가 1~5라면 5는 삭제할 수 없다. 만약 범위가 1~3까지라면 3은 삭제할 수 없다.
remove
함수는 아예 특정 값을 인자로 보내어 해당 값을 삭제할 수 있고, 이 때 특정 범위를 탐색하여 삭제하려는 값을 찾아내어 삭제할 수 있다.
반면 erase
함수의 경우 특정 값을 삭제하려면 해당 값의 위치를 인자로 보내야 삭제할 수 있고, 범위 내에서 특정 값을 찾아내는 것은 불가능하다. 범위가 주어지면 그 범위의 모든 값들을 삭제하는 것만 가능하다.
remove
함수는 주어진 범위( [first, last) - last는 미포함 ) 중에서 삭제되지 않은 가장 마지막 값의 새로운 위치에서 바로 뒤에 있는 위치의 iterator
를 반환해준다.
예를 들어 1 2 3 2 2 4 라는 vector
의 전체 범위 중에서 2를 remove
하면 1 3 4 ... 가 되는데, 1 3 4 ... 에서 4 바로 뒤 위치의 iterator
를 반환해준다는 것이다.
또 예를 들어 1 2 2 3 4 5 라는 vector
의 범위를 2 2 3 이라고 하고 2를 remove
하면 1 3 4 5 ... 가 되는데(실제로는 아니다. 이에 대한 내용은 뒤에 나오는 각 함수의 특징에 대한 설명에 나와있다), 범위 내에서 삭제되지 않은 마지막 값인 3의 바로 뒤 위치 iterator
를 반환해준다는 것이다.
erase
함수는 범위와 상관없이 마지막으로 지워진 값의 위치에서 바로 뒤에 있던 값의 새로운 위치의 iterator
를 반환해준다.
예를 들어 1 2 3 4 5 6 7 이라는 vector
의 범위를 2 3 4라고 하고 erase
하면 2 3 4가 모두 지워지고 1 5 6 7 ... 이 된다(실제로는 아니다. 이에 대한 내용은 뒤에 나오는 각 함수의 특징에 대한 설명에 나와있다). 이 때 기존의 vector
에서 가장 마지막에 지워진 4 바로 뒤에 있던 값이 5이므로, 1 5 6 7 ... 에 있는 새로운 5의 위치 iterator
를 반환해준다는 것이다.
결국 erase
함수보다는, 삭제하고 남은 값들 중 가장 마지막 값의 바로 뒤를 가리키게 되는 remove
함수의 return
값이 더 쓸모있고 의미있어 보인다.
remove
함수를 통해 vector
의 특정 값을 삭제한다고 할 때, 해당 값이 삭제가 되어 그만큼 vector
의 사이즈가 줄어드는 것이 아니라, 삭제된 값이 다른 값으로 대체된다. 이 때 대체되어 들어가는 값은 주어진 범위에서 가장 뒤쪽에 있는 값(들)이 들어간다.
❗ 주의할 점은 그렇다고 해서 삭제된 값이 있던 위치에 그대로 대체되는 값이 들어가는 것이 아니라, 인자로 주었던 범위의 맨 뒤에 들어가게 된다.
예를 들어 vector
에 1 2 3 4 5 가 들어있고 전체 범위에서 2를 삭제한다고 할 때, 2가 1개 삭제되므로 1 3 4 5 뒤에 5(가장 뒤쪽에 있는 값 1개)만 들어가는 것이다. 즉 1 3 4 5 5 가 된다.
또 vector
에 1 2 2 2 3 4 가 들어있고 전체 범위에서 2를 삭제한다고 할 때, 2가 3개 삭제되므로 1 3 4 뒤에 2 3 4(가장 뒤쪽에 있는 값 3개)가 들어가는 것이다. 즉 1 3 4 2 3 4 가 된다.
또한 vector
에 1 2 2 3 4 가 들어있고 범위가 2 2 3 일 때 2를 삭제하면, 2가 2개 삭제되므로 1 3 4 가 되고 범위의 맨 뒤는 3이었으므로 3의 뒤에 대체값이 붙는다. 이 때 대체값은 주어진 범위에서 가장 뒤쪽에 있는 값 2개이므로 2 3 이 된다. 결국 1 3 4 에서 3 뒤에 2 3 이 붙게 되어 1 3 2 3 4 가 된다.
vector
의 특성상 값이 삭제되면 그 삭제된 공간 만큼 앞으로 당겨진다. 그러나 앞에서 말했듯 삭제된 값은 다른 값으로 대체되기 때문에, vector
의 size
나 capacity
는 줄어들지 않고 그대로이다.
예시로 확인해보자.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> v;
// v에 값 0~4 값 넣어주기
for (int i=0; i<5; i++) {
v.push_back(i);
}
// 삭제 전 vector 값들, size, capacity 출력
cout << "삭제 전 vector 값들 : ";
for (int i=0; i<5; i++) {
cout << v[i] << " ";
}
cout << "\n";
cout << "확인을 위해 뒤의 값들 추가로 출력 : ";
for (int i=5; i<10; i++) {
cout << v[i] << " ";
}
cout << "\n";
cout << "size : " << v.size() << "\n";
cout << "capacity : " << v.capacity() << "\n";
cout << "============\n";
// 삭제 : 0~4(처음부터 끝) 값 중 1 없애기
remove(v.begin(), v.end(), 1);
// 삭제 후 vector 값들, size, capacity 출력
cout << "삭제 후 vector 값들 : ";
for (int i=0; i<5; i++) {
cout << v[i] << " ";
}
cout << "\n";
cout << "확인을 위해 뒤의 값들 추가로 출력 : ";
for (int i=5; i<10; i++) {
cout << v[i] << " ";
}
cout << "\n";
cout << "size : " << v.size() << "\n";
cout << "capacity : " << v.capacity() << "\n";
return 0;
}
결과를 보면 1이 정상적으로 삭제되고 앞으로 한칸 당겨졌지만 표시한 흰색 네모의 위치, 즉 범위의 맨 끝 위치에 대체값이 들어갔음을 확인할 수 있다. 그에 따라 size
와 capacity
도 변함이 없음도 확인할 수 있다.
erase
함수를 통해 vector
의 특정 값을 삭제한다고 할 때, 해당 값이 삭제가 되고 그만큼 vector
의 size
가 줄어든다.
해당 값이 지워지면 container
가 모든 요소를 새 위치로 재배치한다. 그렇기 때문에 size
는 줄어도, capacity
는 줄어들지 않는다.
예시로 확인해보자.
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v;
// v에 값 0~4 값 넣어주기
for (int i=0; i<5; i++) {
v.push_back(i);
}
// 삭제 전 vector 값들, size, capacity 출력
cout << "삭제 전 vector 값들 : ";
for (int i=0; i<5; i++) {
cout << v[i] << " ";
}
cout << "\n";
cout << "확인을 위해 뒤의 값들 추가로 출력 : ";
for (int i=5; i<10; i++) {
cout << v[i] << " ";
}
cout << "\n";
cout << "size : " << v.size() << "\n";
cout << "capacity : " << v.capacity() << "\n";
cout << "============\n";
// 0~4(처음부터 끝) 값 중 1 없애기
v.erase(v.begin()+1,v.begin()+2);
// 범위 설명 : 1번 인덱스 ~ 2번 인덱스 전 -> 즉 1번 인덱스 값 하나만 해당
// 삭제 후 vector 값들, size, capacity 출력
cout << "삭제 후 vector 값들 : ";
for (int i=0; i<4; i++) {
cout << v[i] << " ";
}
cout << "\n";
cout << "확인을 위해 뒤의 값들 추가로 출력 : ";
for (int i=4; i<9; i++) {
cout << v[i] << " ";
}
cout << "\n";
cout << "size : " << v.size() << "\n";
cout << "capacity : " << v.capacity() << "\n";
return 0;
}
결과를 보면 1이 정상적으로 삭제되고 앞으로 한칸 당겨졌으며, size
또한 삭제된 만큼 줄었음을 확인할 수 있다.
다만 표시한 흰색 밑줄을 보면, 값이 재할당되어 있다. 그에 따라 capacity
는 변함이 없음을 확인할 수 있다.
두 함수 모두 값을 삭제하면 삭제한 공간만큼 앞으로 당겨지고, 값을 삭제를 해도 capacity
는 유지된다는 것이 공통점이다.
그러나 remove
함수는 size
또한 그대로인 반면, erase
함수는 size
가 삭제된 값의 개수만큼 줄어든다.
remove
함수는 값을 삭제해도 size
가 줄어들지 않는다는 단점이 있다.
하지만 return
값이 남은 값들 중 마지막 값의 바로 뒤를 가리킨다는 장점이 있다.
erase
함수는 return
값이 의미가 없다는 단점이 있다.
하지만 함수의 값을 삭제하면 size
가 줄어든다는 장점이 있다.
앞서 말한 remove
함수와 erase
함수의 단점을 보완하려면, 두 함수의 장점만을 취해서 함께 사용해주면 된다.
우선 코드를 보자.
// vector의 이름이 v라고 가정
v.erase(remove(first, last, 삭제하려는 값),last);
이 코드를 보면, remove
함수를 먼저 수행하고 그 결과를 erase
함수의 시작범위로 사용하고 있음을 알 수 있다. 즉, remove
함수의 return
값이 erase
함수의 시작범위가 되는 것이다.
그런데 remove
함수의 return
값은 삭제되지 않은 가장 마지막 값의 새로운 위치에서 바로 뒤에 있는 위치의 iterator
를 반환해주기 때문에, 결국 범위 내에서 삭제하고 싶지 않은 값들은 냅두고, 그 값들의 바로 뒤부터가 erase
함수의 시작 범위가 된다.
삭제하고 싶지 않은 값들을 erase
해버리면 안되므로 그 뒤부터 erase
하도록 remove
의 return
값을 erase
함수의 시작 범위에 넣어준 것이고, 이것이 바로 remove
함수의 장점을 이용한 것이다.
그러나 remove
함수에서 삭제하고 싶지 않은 값들 바로 뒤에, 삭제한 값의 대체값들이 생긴다. 이 대체값들로 인해 remove
함수가 size
가 줄지 않는다는 단점이 생긴 것이다. 이 쓸모없는 대체값들을 없애주면서 size
또한 그만큼 줄여주어야 한다.
그것을 해주는 것이 바로 erase
함수이다. erase
함수가 삭제하고 싶지 않은 값들의 바로 뒤부터, 즉 대체값들의 시작 범위부터 시작하여 대체값들의 끝 범위까지 값을 삭제해주고 size
또한 줄여준다. 이것이 바로 erase
함수의 장점을 이용한 것이다.
vector
의 특정 범위를 지정하여 해당 범위에 있는 특정 값을 지우고 싶을 때는 아래의 코드를 작성하면 된다.
// vector의 이름이 v라고 가정
v.erase(remove(first, last, 삭제하려는 값),last);
만약 범위를 vector
의 전체로 설정하고 싶다면, 아래와 같이 작성하면 된다.
// vector의 이름이 v라고 가정
v.erase(remove(v.begin(), v.end(), 삭제하려는 값),v.end());
정리 잘해주셔서 감사합니다.
그런데 vector 의 erase() 리턴 값이 의미가 없는 것은 아닙니다..
보통 vector의 리턴값이 지워진 후 새로운 위치의 다음 위치를 가리키는 이유는
탐색을 하며 원소를 삭제하려고할 때 유용하게 쓰이기 때문입니다.
예를 들어 v = {1,2,3,4,5,5,5,5,5,5,5,5,6,7,8,9,10} 이 있다고 한다면 탐색을 하면서 중간에 원소 5들을 삭제하고 싶다면
for (vector<int>::iterator it = v.begin(); it != v.end();) {
if (*it == 5)
it = v.erase(it);
else
it++;
}
이런 식으로 사용하면 편하기 때문이죠.
좋은 글 감사합니다.
헷갈렸는데 정리 잘해주셔서 도움이 되었습니다.