2016. 2. 7. 07:12ㆍ프로그래밍/Effective STL
map과 multimap에 대한 직접적인 키 변경은 불가능하다.(캐스팅만 하지 않으면)
set과 multiset은 그것이 가능하다.
set과 multiset 내의 데이터 요소가 const가 아닌 이유
일반적으로 표준화 위원회의 의도이다.
map 종류는 key만 const면 되고 set은 값이 const가 아니어야 된다는 것.
한 마디로 set은 값 자체 또는 객체 내부의 값이 key 값이 된다.
나머지 자료는 언제든 변환이 가능해야 된다. map은 key가 이미 있으므로 value 값의 경우 뭐든 상관 없다.
하지만
내 환경에선 set으로 설정하면 STL 차원에서 바로 에러를 리턴해줬다.
책에서도 어떤 STL에서는 이 코드를 거부한다더니 내가 사용하는 STL이 이 어떤 STL이었다.
1 2 3 4 5 | // Error // it->SetTitle("Power Boss"); // error C2662: 'Employee::SetTitle' : // cannot convert 'this' pointer from 'const Employee' to 'Employee &' // Conversion loses qualifiers | cs |
반복자 *it가 const이므로 변경할 수 없다는 말이다.
결국 되는 STL도 있고 안 되는 STL도 있다는 뜻.
핵심
여튼 저렇게 되어있지만 어떻게든 Set 내부 객체를 바꿀 수 있는 방법이 있다.
결국 중요한 것은 Set내부의 key가 되는 값을 함부로 변경하면 안 된다는 의미이다.
Set 정렬 자체가 해당 값으로 되기 때문에 컨테이너 자체가 무너지게 된다.
일단 그건 알겠고 그럼 Set의 자료들은 어떻게 바꿀 수 있는가?
방법 1 : cosnt_cast를 사용해 상수성을 없애고 값을 변경한다.
방법 2 : 복사생성자와 erase 후 insert를 해주는 방법.
예시
간단한 Employee 클래스를 설계하고 id를 이용해 Set 컨테이너에 넣고 직원을 관리한다.
https://github.com/ElementalKiss/Cpp/tree/master/Example/SetAndMultisetDoNotChangeKeyValue
Employee 클래스
header
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 50 51 52 53 54 55 56 | #pragma once #include <set> #include <string> #include <functional> #include <iostream> using namespace std; class Employee { public: Employee(void); ~Employee(void); // Conversion Constructor Employee(const int id); Employee(const int id, string name); // Copy Constructor Employee(const Employee& emp); // Getter and Setter inline const int GetId() const { return m_id; } inline void SetId(const int id) { m_id = id; } inline const string& GetName() const { return m_name; } inline void SetName(const string& name) { m_name = name; } inline const string& GetTitle() const { return m_title; } inline void SetTitle(const string& title) { m_title = title; } // Method void PrintInfo() const { cout<<"Selected Employee = Id : "<<GetId()<<", name : "<<GetName()<<", title : "<<GetTitle()<<endl;; } private: // Member Variable int m_id; string m_name; string m_title; }; // Less Function struct IDNumberLess: public binary_function<Employee, Employee, bool> { bool operator() (const Employee& lhs, const Employee& rhs) const { return lhs.GetId() < rhs.GetId(); } }; typedef set<Employee, IDNumberLess> EmplDSet; | cs |
cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include "Employee.h" Employee::Employee(void) : m_id(0), m_name("noname"), m_title("notitle") { } Employee::~Employee(void) { } Employee::Employee(const int id) : m_id(id), m_name("noname"), m_title("notitle") { } Employee::Employee(const int id, string name) : m_id(id), m_name(name), m_title("notitle") { } Employee::Employee(const Employee& emp) : m_id(emp.m_id), m_name(emp.m_name), m_title(emp.m_title) { } | cs |
뭐 대충 이렇게 간단히 Employee 클래스를 작성하고 Set에 사용할 Less function을 작성한다.
Less function에서는 id를 이용하여 값을 비교하여 정렬한다.
main
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 50 51 | #include <iostream> #include "Employee.h" using namespace std; int main(int argc, const char* argv[]) { EmplDSet se; Employee e1(1, "koko"); Employee e2(2, "momo"); Employee e3(3, "popura"); se.insert(e1); se.insert(e2); se.insert(e3); // Method1 : using const_cast EmplDSet::iterator it = se.find(1 /* 1 call conversion constructor : Employee temp(1) */); if (it != se.end()) { // Error // it->SetTitle("Power Boss"); // error C2662: 'Employee::SetTitle' : // cannot convert 'this' pointer from 'const Employee' to 'Employee &' // Conversion loses qualifiers // Using const cast const_cast<Employee&>(*it).SetTitle("Boss"); it->PrintInfo(); } // Method2 : using Copy constructor and erase Employee selectedID(2); EmplDSet::iterator newIt = se.find(selectedID); // 1.find if (newIt != se.end()) { Employee e(*newIt); // 2.copy // avoiding iterator invalidation i++ se.erase(newIt++); // 3.erase // 4.edit e.SetName("Power Momo"); e.SetTitle("New Boss"); se.insert(e); // 5.insert (se.find(2))->PrintInfo(); } return 0; } | cs |
Method2는 책에서 총 5개의 단계로 구구절절 설명하고 있는데
간단하겐 find -> copy -> erase -> edit -> insert 라고 정리하면 되겠다.
'프로그래밍 > Effective STL' 카테고리의 다른 글
[effective STL] 항목 26 : 여러 iterator 중 쓸만한 것은 결국 iterator (2) | 2016.02.10 |
---|---|
[effective STL] 항목 24 : map에서 []나 insert는 효율 문제에 주의하자. (0) | 2016.02.07 |
[effective STL] 항목 23 : 연관 컨테이너 대신 정렬된 벡터를 쓰는 것이 좋을 때도 있다. (2) | 2016.02.07 |
[effective STL] 항목 21 : 연관 컨테이너용 비교 함수는 ==에 대해선 false를 반환해야 한다. (0) | 2016.01.03 |
[effective STL] 항목 20 : 연관 컨테이너에 포인터 넣을 때 비교 함수자 타입을 정해주기 (1) | 2015.12.27 |
[effective STL] 항목 18 : vector<bool> 쓰지마 (1) | 2015.12.26 |
[effective STL] 항목 17 : 용량 바꿔치지 묘수(swap) (0) | 2015.12.26 |