일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 예외
- 다형성
- effective stl
- 참조자
- 암시적 변환
- virtual function
- 루아
- 오블완
- exception
- 티스토리챌린지
- more effective c++
- Smart Pointer
- resource management class
- Vector
- implicit conversion
- UE4
- 언리얼
- reference
- 비교 함수 객체
- 상속
- 게임
- lua
- 영화
- 스마트 포인터
- c++
- operator new
- 영화 리뷰
- Effective c++
- 반복자
- 메타테이블
Archives
- Today
- Total
스토리텔링 개발자
[Effective STL] 20. 컨테이너 비교 함수 객체 본문
728x90
항목 20. 포인터를 저장하는 연관 컨테이너에 대해서는 적합한 비교(비교 함수 객체) 타입을 정해주자
연관 컨테이너 문제
set<string*> ssp;
ssp.insert(new string("Anteater"));
ssp.insert(new string("Wombat"));
ssp.insert(new string("Lemur"));
ssp.insert(new string("Penguin"));
for(set<string*>::const_iterator i = ssp.begin() ; i != ssp.end() ; ++i)
{
cout << *i << endl; // 알파벳 순으로 동물 이름이 출력되지 않을까? 하지만..
}
- set에 들어있는 값은 포인터이기에 출력물은 16진수 네 개가 한 줄씩 나올 뿐이다.
- 아래와 같이 한다면?
// ssp의 문자열을 cout으로 복사한다. 하지만..
copy(ssp.begin(), ssp.end(), ostream_iterator<string>(cout, "\n")); // 컴파일 에러!
- ostream_iterator<string>은 string* 타입 요소의 반복자가 아니므로 컴파일 에러가 발생한다.
- 아래와 같이 한다면?
for(set<string*>::const_iterator i = ssp.begin() ; i != ssp.end() ; ++i)
{
cout << **i << endl; // 이제 문자열이 출력될 것이다. 하지만..
}
- 일반적으로는 알파벳 순서대로 나오지 않는다..
- 포인터 값을 기준으로 정렬되어 나오기 때문이다.
비교 함수 객체 정의
set<string*> ssp;
// 실은 위 코드는 아래의 코드를 생략하고 쓴 것이다.
set< string*, less<string*>/*비교 함수 객체*/, allocator<string*>/*할당자*/ > ssp;
// string*을 알파벳 순으로 늘어서도록 할 때는 기본 비교 함수 객체(less)를 쓸 수 없다.
// 커스텀 해보자.
/*
struct StringPtrLess : public binary_function<const string*, const string*, bool>
{
bool operator()(const string* ps1, const string* ps2) const
{
return *ps1 < *ps2;
}
};
모던 c++에서는 binary_function이 삭제되었으므로 아래와 같이 쓰자.
*/
struct StringPtrLess
{
bool operator()(const std::string* ps1, const std::string* ps2) const
{
return *ps1 < *ps2;
}
};
// typedef set<string*, StringPtrLess> StringPtrSet;
// 대신 using을 사용하자.
using StringPtrSet = std::set<std::string*, StringPtrLess>;
StringPtrSet ssp;
for(StringPtrSet::const_iterator i = ssp.begin() ; i != ssp.end() ; ++i)
{
// 이제 알파벳 순으로 출력된다.
cout << **i << endl;
}
- for_each로 for 문을 더 깔끔하게 바꿔보자.
void print(const string* ps)
{
cout << *ps << endl;
}
for_each(ssp.begin(), ssp.end(), print);
// 아래처럼 람다를 사용해도 된다.
for_each(ssp.begin(), ssp.end(), [](const string* ps)
{
cout << *ps << endl;
});
- transform과 ostream_iterator로 더 깔끔하게 만들어보자.
// 이 타입의 함수 객체에 T*이 넘겨지면, const T&를 반환한다.
struct Dereference
{
template<typename T>
const T& operator()(const T* ptr) const
{
return *ptr;
}
};
// ssp의 요소를 역참조용 함수 객체(Dereference())를 통해 변환하고, 그 결과를 cout으로 보낸다.
transform(ssp.begin(), ssp.end(), ostream_iterator<string>(cout, "\n"), Dereference());
비교 '타입'(comparison type)
- 아무튼 핵심은, 포인터를 담는 연관 컨테이너는 포인터 값을 기준으로 정렬된다는 것을 기억해야 한다는 것이다.
- 보통은 포인터를 기준으로 정렬되는 것을 원하지 않을 것이다.
- 그러므로 비교 타입으로 동작할 함수 객체 클래스를 거의 항상 만들어야 할 것이다.
- 비교 '타입'? 그냥 set 요소 비교 함수를 만들면 되는 거 아닌가?
// 타입이 아니라 함수로 만들어 본다.
bool stringPtrLess(const string* ps1, const string* ps2)
{
return *ps1 < *ps2;
}
set<string, stringPtrLess> ssp; // 하지만 컴파일이 안 된다.
- 하지만, 템플릿에 들어가는 세 매개 변수(타입, 비교자, 할당자)는 타입(type)이어야 한다는 제약이 있다.
- 결국 필요해진다면, 포인터를 담는 연관 컨테이너를 위한 범용 비교 타입이 있으면 좋을 것이다.
struct DereferenceLess
{
template<typename PtrType>
bool operator()(PtrType pT1, PtrType pT2) const
{
return *pT1 < *pT2;
}
};
// 사용만 하면 된다.
set<string*, DereferenceLess> ssp;
추가
- 연관 컨테이너에 포인터가 아니라 포인터처럼 동작하는 것이 들어가도 마찬가지이다.
- 예컨대 스마트 포인터, 반복자 같은 것들.
- 다행히 위에서 만든 DereferenceLess 타입은, 이들에도 잘 동작한다.
728x90
'개발 > Effective STL' 카테고리의 다른 글
[Effective STL] 22. set 요소의 key 바꾸기 금지 (0) | 2024.12.05 |
---|---|
[Effective STL] 21. 연관 컨테이너 비교 함수 객체의 동일값 비교 (0) | 2024.12.04 |
[Effective STL] 19. 상등(equality)과 동등(equivalence) 관계 (0) | 2024.11.29 |
[Effective STL] 18. vector<bool> 주의 (0) | 2024.11.27 |
[Effective STL] 17. swap 트릭으로 vector 용량 줄이기 (0) | 2024.11.26 |
Comments