v1 벡터를 v2의 중간 이후의 요소로 만드는 가장 빠른 방법
assign은 표준 시퀀스 컨테이너(vector, string, deque, llist) 모두 사용 가능
v1.assign(v2.begin() + v2.size() / 2, v2.end());
삽입 연산자(inserter, back_inserter, front_inserter 등)를 사용해서 복사 대상 범위를 지정하는 copy는 멤버 함수로 변경하는 것이 가능
copy의 경우 복사에 초점이 맞추어져 있지만 insert를 통해 삽입의 의미를 명확하게 나타낼 수 있음
v1.insert(v1.end(), v2.begin() + v2.size() / 2, v2.end());
단일 요소 멤버 함수보다 범위 멤버 함수가 더 좋은 이유
코드가 대개 짧아 작성하기 쉽고, 명확하고 간결한 의미를 전달하여 이해하기 쉬워 유지보수성이 올라감
불필요한 연산/복사 방지
인덱스가 증가하는 연산
반복자를 계속 할당하는 이유는 삽입하면 반복자가 무효화되고, 만약 무효화되지 않아도 매번 벡터의 앞에 추가되는 것을 방지하기 위함
// 단일 멤버 함수
vector<int>::iterator insertLoc(v.begin());
for (int index = 0; index < numValues; ++index)
{
insertLoc = v.insert(insertLoc, data[index]);
}
// 범위 멤버 함수
v.insert(v.end(), data.begin(), data.end());
불필요한 함수 호출 방지
insert가 numValue번 호출되고, insert는 1번 호출되므로 numValue-1만큼 줄어듦
만약 인라인된다면 호출 횟수는 감소하지만 코드 크기 증가
불필요한 이동 방지
맨 뒤에 삽입하는 경우가 아니므로 삽입할 때마다 numValue번 n 개의 요소들이 이동해야 하는 것을 방지 ( n * numValue 이동 감소)
복사나 이동 비용이 큰 객체를 담고 있다면 비용이 더 증가하는 것을 방지
범위 멤버 함수를 사용한다면 마지막 위치까지 바로 옮기므로 n번의 이동 횟수뿐
- 단 반복자 사이의 거리를 알 수 있어야 하는데, 순방향 반복자를 지원하지 않는 입출력 반복자(istream_iterator 등)를 제외하면 모두 가능
불필요한 메모리 할당 및 해제 방지
iterator와 인덱스를 가리키는 int 변수 생성 및 해제
메모리가 꽉 찰때마다 capacity를 두 배 늘리는 과정에서 새 메모리 할당 및 이동/복사 및 기존 메모리 해제가 일어나므로 log(numValue)번의 메모리 할당 방지
범위 멤버 함수는 필요한 메모리량을 예측할 수 있기 때문에 한 번만 메모리 할당이 발생
위의 내용은 vector에 관한 것으로, string도 동일
deque는 다른 메모리 관리 방식을 취하므로 불필요한 메모리 할당 및 해제 방지 내용은 해당하지 않음
list는 노드 기반으로 동작하므로 불필요한 메모리 할당 및 해제 방지 내용은 해당하지 않지만 새로운 문제 방지
삽입되는 위치 다음의 노드를 A라고 할 때, 삽입되는 노드의 next 포인터는 A를 가리키다 삽입되는 노드를 가리키고, A의 prev는 삽입되는 노드를 가리키므로 2 * (numValues - 1)번의 불필요한 연산 방지
위의 내용은 insert 함수 효율 분석으로 erase도 비슷하지만 erase는 요소 수가 줄어들 때 자동으로 메모리를 해제하지 않음
범위 멤버 함수
InputIterator는 어떤 입력 반복자도 받아들일 수 있는 타입
모든 표준 컨테이너는 범위로 생성할 수 있는 생성자가 있음
container::container(InputIterator begin, InputIterator end);
모든 표준 컨테이너는 범위로 삽입할 수 있는 멤버 함수가 있음
// 시퀀스 컨테이너
void container::insert(iterator position, InputIterator begin, InputIterator end);
// 연관 컨테이너(위치는 알아서 정함)
void container::insert(InputIterator begin, InputIterator end);
모든 표준 컨테이너는 범위로 삭제할 수 있는 멤버 함수가 있음
iterator container::erase(iterator begin, iterator end);
모든 표준 시퀀스 컨테이너는 범위 할당 멤버 함수가 있음
void container::assign(InputIterator begin, InputIterator end);
'C++ > Effective STL' 카테고리의 다른 글
Effective STL 항목 7 new로 생성한 포인터의 컨테이너를 사용할 때에는 컨테이너가 소멸되기 전에 포인터를 delete하는 일을 잊지 말자 (0) | 2024.12.18 |
---|---|
Effective STL 항목 6 C++ 컴파일러의 어이없는 분석 결과를 조심하자 (0) | 2024.12.17 |
Effective STL 항목 3 복사는 컨테이너 안의 객체에 맞게 비용은 최소화하며, 동작은 정확하게 하자 (0) | 2024.12.15 |
Effective STL 항목 2 컨테이너에 독립적인 코드라는 환상을 조심하자 (0) | 2024.12.10 |
Effective STL 항목 1 적재적소에 알맞은 컨테이너를 사용하자 (0) | 2024.12.09 |