map이나 multimap 등의 [] 연산자는 두가지 동작을 같는다. 첫번째는 해당 키값의 데이타를 갱신하는 것이고 두번째로 해당 키값의 데이타가 컨테이너에 존재하지 않는다면 추가를 하는 동작이다.
문제는 추가를 하는 동작에서의 불필요한 오버해드이다.
이 오버헤드는 일반적으로 기본생성자가 아닌 특정 데이타 타입을 지원의 생성자를 지원하는 클래스에서 문제가 되는데
같은 식으로 선언된 클래스의 경우
위의 코드는 실질적으로 기본 생성자를 통해서 임시 Widget 변수를 생성하고 그걸 map에 추가하고 그 후에 키값이 1인 항목을 찾아 수정하는 과정을 거치게 된다.
Widget 객체를 임시로 만드는데 필요한 기본 생성자 함수, 이것을 없에는 대 필요한 소멸자 함수, 그리고 Widget의 대입 연산자 함수가 추가 적으로 호출된다.
이 오버헤드는 일반적으로 기본생성자가 아닌 특정 데이타 타입을 지원의 생성자를 지원하는 클래스에서 문제가 되는데
class Widget{
public:
Widget();
Widget(double weight);
Widget& operator=(double weight);
}
map<int, Widget> m;
m[1] = 1.50;
typedef map<int, Widget> IntWidgetMap;
pair<IntWidgetMap::iterator, bool> result = m.insert(IntWidgetMap::value_type(1, Widget()));
result.first->second = 1.50;
[] 연산으로 삽입이 될경우 이런 오버헤드를 얻게 될수 있지만 명시적으로 insert를 사용한다면 해당 오버해드를 줄일수 있다.
m.insert(IntWidgetMap::value_type(1,1.50));
insert 함수를 이용한 갱신을 할경우는 위와는 반대의 일이 일어난다. insert함수를 위한 추가적인 생성자가 호출되고 이후 대입연산이 이루어 진다.
// [] 연산자를 이용한 갱신
m[k] = v;// insert를 이용한 갱신
m.insert(IntWidgetMap::value_type(k,v)).first->second = v;
결론을 내리자면 map에서 insert와 [] 적절한 위치에 사용을 해야 하며 만약 해당 키값의 데이타가 존재하는지 모를 경우에는 lower_bound 등의 검색함수를 이용하여 해당 데이타가 존재하지 않는다면 insert를 호출, 존재한다면 갱신하는 루틴을 작성하도록 한다.
(lower_bound 의 리턴값은 약간 특이. EffectiveSTL45 참조)
(lower_bound 의 리턴값은 약간 특이. EffectiveSTL45 참조)