C++ 기초부터 마스터까지 — 11화
람다와 표준 알고리즘 사용하기
본 블로그 작성 글들은, 강의를 목표로 하고 제작되었습니다. 또한 작성된 글의 정보도 틀렸을 수 있으니, 주의해주세요.
컨테이너에서 특정 몬스터를 찾거나 정렬하고 죽은 몬스터를 제거하는 코드는 자주 등장한다. for문으로 직접 만들 수도 있지만 표준 알고리즘을 사용하면 코드의 목적이 더 잘 보인다.
람다
람다는 이름 없이 바로 작성하는 함수이다.
int monsterId = 10;
auto isTarget = [monsterId](const std::shared_ptr<Monster>& monster)
{
return monster->GetId() == monsterId;
};
[]는 캡처 목록이다. [monsterId]는 값을 복사해서 캡처하고, [&]는 외부 변수를 참조로 캡처한다. [this]는 현재 객체를 사용한다.
참조 캡처는 편하지만 람다가 원본 변수보다 오래 살아남으면 위험할 수 있다. 필요한 값만 명확하게 캡처하는 편이 코드를 읽기 쉽다.
find_if
auto iter = std::find_if(
activeMonsters.begin(),
activeMonsters.end(),
[monsterId](const auto& monster)
{
return monster->GetId() == monsterId;
}
);
if (iter != activeMonsters.end())
{
currentTarget = *iter;
}
find_if는 조건을 만족하는 첫 원소의 반복자를 반환한다. 찾지 못하면 전달한 범위의 end를 반환한다.
sort
체력이 낮은 몬스터부터 정렬하고, 체력이 같으면 ID가 낮은 몬스터를 먼저 둘 수 있다.
std::sort(
activeMonsters.begin(),
activeMonsters.end(),
[](const auto& a, const auto& b)
{
if (a->GetHealth() != b->GetHealth())
{
return a->GetHealth() < b->GetHealth();
}
return a->GetId() < b->GetId();
}
);
비교 함수가 true를 반환하면 a가 b보다 앞에 와야 한다는 뜻이다. 비교 기준이 서로 모순되면 정렬 결과를 믿을 수 없으므로 엄격 약순서를 지켜야 한다.
remove_if와 erase
auto newEnd = std::remove_if(
activeMonsters.begin(),
activeMonsters.end(),
[](const auto& monster)
{
return monster->IsDead();
}
);
activeMonsters.erase(newEnd, activeMonsters.end());
remove_if는 컨테이너 크기를 직접 줄이지 않는다. 남길 원소를 앞으로 옮기고 새로운 논리적 끝을 반환한다. 실제 삭제는 erase가 수행한다.
vector에서 원소가 삭제되면 뒤쪽 반복자와 참조가 무효화될 수 있다. shared_ptr 원소가 제거되면 참조 횟수가 감소하고, 마지막 소유자였다면 객체도 파괴된다.
람다와 알고리즘을 사용한다고 코드가 무조건 빨라지는 것은 아니다. 대신 찾기, 정렬, 제거라는 의도가 코드에 그대로 나타나는 것이 가장 큰 장점이다.