[C++] vector의 값 지우기 - remove() 함수 vs. vector의 erase() 함수

ᴄsᴇ ᴘᴇʙʙʟᴇ·2022년 4월 3일
2

C++ 공부노트

목록 보기
2/2
post-thumbnail
post-custom-banner

💻 remove() 함수와 erase() 함수 비교하기

✏️ 선언해주어야 하는 헤더

remove 함수는 algorithm 헤더를 선언해주어야 한다.

erase 함수는 vector의 함수들 중 하나이기 때문에, vector 헤더를 선언해주어야 한다.

✏️ 기본 형태

remove 함수의 기본 형태

remove(first, last, 삭제하려는 값); // 범위는 [first,last)

remove 함수는 특정 범위를 탐색하여 그 범위에 있는 특정 값을 삭제할 수 있다.

erase 함수의 기본 형태

// vector의 이름이 v라고 가정

// 1
v.erase(first, last); // 범위는 [first,last)

// 2
v.erase(삭제하려는 값의 위치);

erase 함수는 특정 범위에 속하는 모든 값들을 삭제하거나(해당 범위에서의 특정 값은 삭제 불가능), 특정 값의 위치를 보내면 해당 값을 삭제할 수 있다

두 함수의 공통점

둘 다 firstlast에 포함되는 범위가 [first, last)이다. last는 범위에 포함되지 않는다는 것을 기억하자!

그리고 두 함수 모두 맨 끝 범위에 위치한 값은 삭제할 수 없다!
ex) 1 2 3 4 5 가 있을 때, 범위가 1~5라면 5는 삭제할 수 없다. 만약 범위가 1~3까지라면 3은 삭제할 수 없다.

두 함수의 차이점

remove 함수는 아예 특정 값을 인자로 보내어 해당 값을 삭제할 수 있고, 이 때 특정 범위를 탐색하여 삭제하려는 값을 찾아내어 삭제할 수 있다.

반면 erase 함수의 경우 특정 값을 삭제하려면 해당 값의 위치를 인자로 보내야 삭제할 수 있고, 범위 내에서 특정 값을 찾아내는 것은 불가능하다. 범위가 주어지면 그 범위의 모든 값들을 삭제하는 것만 가능하다.

✏️ 두 함수의 반환값

remove 함수의 반환값

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를 반환해준다는 것이다.

earse 함수의 반환값

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 함수의 특징

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의 특성상 값이 삭제되면 그 삭제된 공간 만큼 앞으로 당겨진다. 그러나 앞에서 말했듯 삭제된 값은 다른 값으로 대체되기 때문에, vectorsizecapacity는 줄어들지 않고 그대로이다.

예시로 확인해보자.

#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이 정상적으로 삭제되고 앞으로 한칸 당겨졌지만 표시한 흰색 네모의 위치, 즉 범위의 맨 끝 위치에 대체값이 들어갔음을 확인할 수 있다. 그에 따라 sizecapacity도 변함이 없음도 확인할 수 있다.

erase 함수의 특징

erase 함수를 통해 vector의 특정 값을 삭제한다고 할 때, 해당 값이 삭제가 되고 그만큼 vectorsize가 줄어든다.

해당 값이 지워지면 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의 값 쉽게 지우기

앞서 말한 remove 함수와 erase 함수의 단점을 보완하려면, 두 함수의 장점만을 취해서 함께 사용해주면 된다.

우선 코드를 보자.

// vector의 이름이 v라고 가정

v.erase(remove(first, last, 삭제하려는 값),last);

이 코드를 보면, remove 함수를 먼저 수행하고 그 결과를 erase 함수의 시작범위로 사용하고 있음을 알 수 있다. 즉, remove 함수의 return 값이 erase 함수의 시작범위가 되는 것이다.

그런데 remove 함수의 return 값은 삭제되지 않은 가장 마지막 값의 새로운 위치에서 바로 뒤에 있는 위치의 iterator를 반환해주기 때문에, 결국 범위 내에서 삭제하고 싶지 않은 값들은 냅두고, 그 값들의 바로 뒤부터가 erase 함수의 시작 범위가 된다.

삭제하고 싶지 않은 값들을 erase 해버리면 안되므로 그 뒤부터 erase 하도록 removereturn 값을 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());

💻 참고 링크

C++ 공식 문서 - remove, erase
참고 블로그

profile
ꜱɪɴᴄᴇ 2021.09.01
post-custom-banner

2개의 댓글

comment-user-thumbnail
2023년 1월 7일

헷갈렸는데 정리 잘해주셔서 도움이 되었습니다.

답글 달기
comment-user-thumbnail
2024년 4월 12일

정리 잘해주셔서 감사합니다.

그런데 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++;
}

이런 식으로 사용하면 편하기 때문이죠.
좋은 글 감사합니다.

답글 달기