2016. 2. 7. 20:44ㆍ프로그래밍/Effective STL
1 2 3 4 5 6 7 8 9 10 11 | class Widget { public: Widget(); Widget(double weight); Widget& operator=(double weight); ... }; ... map<int, Widget> mW; | cs |
double 값을 이용하여 widget을 초기화 할 수 있다.
이렇게 자료구조가 있을 때 두 가지 정도 작업이 있을 수 있다.
1. map에 값을 넣으며 초기화.
2. map에 들어가 있는 값을 변경하는 작업.
보통은 둘 다 이렇게 사용한다.
1 | mW[1] = 1.50; | cs |
map의 operator[] 연산자의 특징
추가 아니면 갱신(add or update) 기능을 수행하도록 설계되어 있다.
1 | mW[k] = v; | cs |
키 값 k가 들어 있는지 점검 -> 그렇지 않다면 k와 v를 페어로 묶어 맵에 새로 추가한다.
만약 k가 있다면 -> k와 맵핑된 value 값을 v로 갱신한다.
map에 값을 넣으면 초기화 할 때
1 | mW[1] = 1.50; |
이것을 풀어 쓰게 되면 이렇게 된다고 한다.
1 2 3 4 5 6 | typedef map<int, Widget> IntWidgetMap; pair<IntWidgetMap::iterator, bool> result = mW.insert(IntWidgetMap::value_type(1, Widget())); result.first->second = 1.50; | cs |
Widget의 기본 생성자를 호출하여 만들고 이것을 대입하는 형태가 된다.
이렇게 하지 말고 아예 Widget 객체를 원하는 값을 생성자 매개 변수에 넣어 바로 만들어 넣는것이 효율적이다.
1 | m.insert(IntWidgetMap::value_type(1,1.50)); | cs |
이렇게 하면 임시로 만드는 기본생성자와 대입 연산자가 호출되지 않으므로 비용을 크게 줄일 수 있다.
map에 들어가 있는 값을 변경 할 때
1 2 3 | m[k] = v; // 1 m.insert(IntWidgetMap::value_type(k,v)).first->second = v; // 2 | cs |
key가 이미 있기 때문에 operator[]는 pair 객체를 생성하지 않는다.
효율적으로나 미적으로나 // 1이 낫다.
결론
"추가"를 할 경우는 insert가 효율적이다. "갱신"을 할 경우는 operator[]가 효율적이다.
EfficientAddOrUpdate 만들기
그럼 insert와 []를 구분하여 서로 다르게 동작하는 함수를 만들면 될 것이다.
https://github.com/ElementalKiss/Cpp/blob/master/Example/EfficientAddOrUpdate.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #include <iostream> #include <map> using namespace std; class Widget { public: Widget() : m_weight(0) { }; Widget(double weight) : m_weight(weight) { }; Widget& operator=(double weight) { this->m_weight = weight; return *this; }; private: double m_weight; }; template<typename MapType, typename KeyArgType, typename ValueArgType> typename MapType::iterator efficientAddOrUpdate(MapType& m, const KeyArgType& k, const ValueArgType& v) { typename MapType::iterator lb = m.lower_bound(k); if (lb != m.end() && !(m.key_comp()(k,lb->first))) { lb->second = v; return lb; } else { typedef typename MapType::value_type MVT; return m.insert(lb, MVT(k,v)); } } int main(int argc, const char* argv[]) { map<int, Widget> mW; mW[1] = 1.5; // goto insert efficientAddOrUpdate(mW, 2, 1.6); // goto [] efficientAddOrUpdate(mW, 1, 1.2); return 0; } | cs |
'프로그래밍 > Effective STL' 카테고리의 다른 글
[effective STL] 항목 28 : reverse_iterator 쓸 때 base() 잘 쓰기 (0) | 2016.02.19 |
---|---|
[effective STL] 항목 27 : const_iterator -> iterator는 distance와 advance를 사용하자. (2) | 2016.02.15 |
[effective STL] 항목 26 : 여러 iterator 중 쓸만한 것은 결국 iterator (2) | 2016.02.10 |
[effective STL] 항목 23 : 연관 컨테이너 대신 정렬된 벡터를 쓰는 것이 좋을 때도 있다. (2) | 2016.02.07 |
[effective STL] 항목 22 : set과 multiset의 키를 바꾸지 말자. (1) | 2016.02.07 |
[effective STL] 항목 21 : 연관 컨테이너용 비교 함수는 ==에 대해선 false를 반환해야 한다. (0) | 2016.01.03 |
[effective STL] 항목 20 : 연관 컨테이너에 포인터 넣을 때 비교 함수자 타입을 정해주기 (1) | 2015.12.27 |