스토리텔링 개발자

[Effective STL] 33. 포인터 요소에 remove 알고리즘 사용 주의하기 본문

개발/Effective STL

[Effective STL] 33. 포인터 요소에 remove 알고리즘 사용 주의하기

김디트 2024. 12. 20. 10:47
728x90

항목 33. remove와 비슷한 알고리즘을 포인터의 컨테이너에 적용할 때에는 각별히 조심하자

 

 

 

포인터 요소 제거
  • 품질 보증 함수를 기준으로 벡터의 Widget 포인터 요소를 삭제하는 코드
class Widget
{
public:
...
    bool isCertified() const; // Widget의 품질이 확실한가
...
};

vector<Widget*> v;
...
v.push_back(new Widget);
...
// 품질 보증이 되지 않은 Widget 객체 삭제
// mem_fun(항목 41 참조)
v.erase(remove_if(v.begin(), v.end(), not1(mem_fun(&Widget::isCertified))), v.end());
  • 만약 remove_if 호출 전 상황이 아래와 같다면

보증이 되지 않은 Widget은 uncertified로 표시

  • remove_if 호출 후에는 아래와 같이 될 것이다.

remove_if 호출 후

  • 여기에 erase까지 호출된다면 아래와 같이 될 것이다.

erase 호출 후

  • WidgetB와 WidgetC는 delete가 되지 않고 포인터를 잃어버렸다.
    • 즉, 리소스 누수가 발생한다.
  • 결국, 포인터에 대해서는 remove와 흡사한 알고리즘은 리소스 누수를 유발한다는 결론.
    • 대신 partition 알고리즘(항목 31 참조)을 사용하면 될 것이다.

 

 

 

remove 알고리즘을 굳이 사용하고자 한다면
  • 방법 1 : erase-remove 를 호출하기 전에 포인터를 delete하고 nullptr로 셋팅하기
void delAndNullifyUncertified(Widget*& pWidget)
{
    if(!pWidget->isCertified())
    {
        delete pWidget;
        pWidget = nullptr;
    }
}

// 보증되지 않은 객체 널 포인터 셋팅
for_each(v.begin(), v.end(), delAndNullifyUncertified);

// 널 포인터 제거
v.erase(remove(v.begin(), v.end(), nullptr), v.end());
  • 하지만 이 코드는, 불필요한 널 포인터는 가지지 않는다는 가정하에 만든 것이라는 단점이 있다.
  • 방법 2 : 스마트 포인터 사용
vector< shared_ptr<Widget> > v;
...
v.push_back(make_shared<Widget>(new Widget)); // 스마트 포인터 사용
...
// 누수 없이 remove-erase를 사용할 수 있다.
v.erase(remove_if(v.begin(), v.end(), not1(mem_fun(&Widget::isCertified))), v.end();
  • 대신 스마트 포인터가 Widget*으로 자동 변환을 지원해 주어야 한다.
    • 호출되는 멤버 함수(Widget::isCertified)가 내장 포인터를 요구하기 때문이다.
728x90
Comments