[effective STL] 항목 33 : remove 같은 알고리즘 사용할 때 포인터 컨테이너 주의

2016. 2. 29. 23:08프로그래밍/Effective STL

728x90
728x90

동적으로 할당한 객체를 관리하는 컨테이너가 있을 때 주의해야 한다는 것이다.


상황(예시)

widget 객체를 동적으로  여러개 선언하고 

이를 관리하는 v라는 widget 포인터 백터가 있다고 가정한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
clss Widget
{
public:
    ...
    bool isCertified() const;
    ...
};
 
vector<Widget* > v;
 
...
v.push_back(new Widget);
...
cs


widget 객체 중에 certified 되지 않은 위젯을 지우고 싶다!


1
2
v.erase(remove_if(v.begin(), v.end(), 
    not1(mem_fun(&Widget::isCertified))), v.end());
cs


not1과 mem_fun 함수들을 사용해 본 적은 없는데.. 대충 아닐때와 맴버 변수 호출 느낌인 것 같다.


32장에서 배운 것처럼 이렇게 하면 될 것 같지만 하지만 세상은 그리 쉽지 않다.


widget2와 widget3이 uncertified일때 이 위젯을 삭제하면



remove 명령을 하면 아래처럼 되는데.. 제거되는 포인터에 제거되지 않는 포인터 값을 덮어버린다.

그럼 widget2, widget3을 가리키는 포인터는 안드로메다로..... 그리고 객체는 낙동강 오리알이 된다.


이 상태로 erase를 실행하게 되면 widget2,widget3은 파워 누수가!!


그럼 삭제할 땐 어떻게 해야되나?

방법 1. uncertified 된 객체를 미리 nullptr로 만들어 루프를 돌리고 erase-remove 명령을 nullptr된 객체로 돌린다.

방법 2. 스마트 포인터를 사용한다.


방법 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// nullptr function
void nullify_uncertified(Widget*& pWidget)
{
    if (!pWidget->isCertified()) {
        delete pWidget;
        pWidget = nullptr;
    }
}

... 

for_each (v.begin(), v.end(), nullify_uncertified);
 
v.erase(remove(v.begin(), v.end(), static_cast<Widget*>(0)), v.end());
cs


nullptr로 만들어 버리는 함수를 선언한 다음 for_each로 이걸 돌린다.

그리고 erase-remove 합성문을 사용하면 됨.


방법 2

객체를 push_back 하는 곳에서 smart_ptr로 감싸는 방법이다.

1
2
3
4
5
6
7
8
9
template <class T>
class SMART { ... };
typedef SMART<Widget> SMART_WIDGET;
vector<SMART_WIDGET> v;
...
    v.push_back(SMART_WIDGET(new Widget));
...
    v.rease(remove_if(v.begin(), v.end(),
        not1(mem_fun(&Widget::isCertified))), v.end());
cs


728x90
반응형