set과 multiset에 저장된 데이터 요소에 대해 키(key)를 바꾸는 일은 피하자

우선 알아둬야 할 사항은 set과 multiset의 경우 하나의 값을 넣는대 그 값이 순서를 유지하는 역할인 Key값이 되므로 변경해서는 안된다. 하지만 구조체나 클래스 일경우 그 일부분만 Key가 되고 나머지 부분은 그저 데이타일 뿐이므로 변경을 해도 set의 순서가 엉망이 되는 경우는 없다. 그렇게 때문에 일반적으로 set의 데이타타입은 변경이 가능하도록 const 형이 아니도록 되어있다.

map과 multimap의 경우는 두개의 테이타타입을 넣고 그중 Key는 const 타입이 된다.

하지만 위의 사항에서 문제점은 일부 STL의 경우 set의 데이타타입이 const가 아니라고 해도 컨테이너에 저장된 데이타요소의 변경을 막도록 구현되 있는 경우가 있다는 것이다.
set<T>::iterator 에 대한 operator* 연산자가 const T& 를 반환
(모호한 표준안에 의해 다른 해석을 한 STL구현방식 이라고 설명하고 있음)

이런 경우에 대비하고 이식성을 고려한다면 set, multiset의 요소값을 변경하지 못한다고 가정하는 것이 옳다. 하지만 약간의 편법이 cast를 이용해서 이 방법을 빗겨나갈수 있다.
// Object는 set에서 Key역할을 하는 ID와 기타 데이타로 구성
set<Object>::iterator i = se.find( objectID );

// 밑의 코드는 일부 STL에서는 적용되지 않는다.
if( i != se.end() ) {
i->SetName(“yoway”); // Key가 아닌 데이타는 변경하고 싶다.
}

// 아래처럼 캐스팅을 사용한다.
if( i != se.end() ) {
const_cast<Object&>(*i).SetName(“yoway”);
}

// 아래처럼 하면 안된다.
if( i != se.end() ) {
static_cast<Object>(*i).SetName(“yoway”);
}

첫번째 if문은 특정 STL에서는 안될 가능성이 있다. 두번째 if문을 사용하면 해결이 가능하지만 세번째 if문은 캐스팅을 한 결과가 임시객체이기 때문에 임시객체를 변경해도 부질없다.(그렇기 때문에 참조자로 캐스팅)

캐스팅을 사용하고 싶지 않다면







  1. 변경하고자 하는 컨테이너 요소의 위치를 찾는다.(EffectiveSTL45 참조)
  2. 변경하고자 하는 요소의 복사본을 만든다. (map의 Key를 변경하는 경우는 복사본의 Key의 경우는 const로 하지 않는다.)
  3. 컨테이너에서 해당 요소를 삭제한다. (EffectiveSTL09 참조)
  4. 만들어둔 복사본의 정보를 원하는대로 바꾼다.
  5. 복사본을 컨테이너에 삽입한다. 만약 새로 삽입되는 복사본의 위치가 제가된 요소의 위치와 같거나 그 옆(즉 Key같이 변경되지 않았을 경우)이면 윗 단계에서 요소의 위치를 파악했던 반복자를 이용해서 삽입함으로써 상수시간에 삽입이 가능하다.



// Object는 set에서 Key역할을 하는 ID와 기타 데이타로 구성
set<Object>::iterator i = se.find( objectID );
if( i != se.end() ) {
Object o(*i);
se.erase(i++);

o.SetName(“yoway”);
se.insert(i,o);
}

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다