Exceptional C++ Style : 6. 여러수준의 일반성, 2부: 충분히 일반적인가?
첫번째 문제
아래 코드를 실행해 보면,
#include <stdio.h>
#include <list>
template <class T>
void destroy(T* p) {
p->~T();
}
class Integer {
public:
Integer(int value) : p(value) {printf("create Interger\n");}
~Integer() { printf("destroy Interger\n"); }
int getValue() {return p;}
int p;
};
template<typename T>
class Fwdlter : public std::list<T>::iterator
{
T* p;
public:
Fwdlter(T* x) :p(x) {}
~Fwdlter() { printf("iterator destroy\n");}
Fwdlter(const Fwdlter& mit) : p(mit.p) {}
Fwdlter& operator++() {++p;return *this;}
Fwdlter operator++(int) {Fwdlter tmp(*this); operator++(); return tmp;}
bool operator==(const Fwdlter& rhs) {return p==rhs.p;}
bool operator!=(const Fwdlter& rhs) {return p!=rhs.p;}
T& operator*() {return *p;}
};
template <class Fwdlter>
void destroy(Fwdlter first, Fwdlter last) {
while (first != last) {
destroy(first);
++first;
}
}
int main() {
std::list<Integer*> temp;
temp.push_back(new Integer(1));
temp.push_back(new Integer(2));
temp.push_back(new Integer(3));
temp.push_back(new Integer(4));
destroy(temp.begin(), temp.end());
return 0;
}
|
결과는 ?
error:
no matching function for call to ‘destroy(std::_List_iterator<Integer*>&)’
|
- first는 std::list<Integer*>::iterator 객체이기 때문이다.
void destroy()내의 destroy(first)를 destroy(*first)로 바꾸면 성공! 이유는?
- iterator의 T& operator*() {return *p;} 멤버 함수로 인해 Integer*가 return되기 때문
그럼 main이 아래와 같을 경우는?
int main() {
std::list<Integer> temp2;
temp2.push_back(1);
temp2.push_back(2);
temp2.push_back(3);
temp2.push_back(4);
destroy(temp2.begin(), temp2.end());
return 0;
}
|
- destroy의 param을 Integer* type으로 변경 해주면 된다.
- *first 는 Interger type 이므로 이것을 pointer type으로 변경하기 위해 &을 추가한다. 즉destroy(&*first); 가 된다.
- 하지만 이것도 &를 Integer class에서 재정의 하고 있다면 오동작이 발생할 수 있다. (사실 이런 경우는 거의 없다.)
두번째 문제
template <class T>
void swap(T& a, T& b) {
T temp(a);
a = b;
b = temp;
}
|
위코드를 사용하기 위한 T의 요구 조건 :
- T temp(a); 를 위한 복사 생성자 제공
- a = b; 를 위한 operator=() 재정의
- 예외 안정성을 고려 : 위는 예외에 전혀 안정적이지 않음
- 만약 a=b를 수행하고 b=temp 수행시 exception 이 발생할 경우 a만 변경된 상태에서 swap을 빠져나가는 경우 발생
- T가 atomic이 아닐 경우 a 혹은 b가 일부 변경된 상태로 swap을 빠져나가는 경우 발생
2번 조건을 제거하는 방법을 사용 시 더 나은 예외 안정성까지 제공됨
member function으로 예외를 던지지 않은 swap을 가지고 있는 class가 있을 경우 표준 함수를 다음과 같이 특수화 하여 예외 안정성까지 개선할 수 있다.
class MyClass {
public:
void Swap(MyClass& ) /* throw() */;
//…
};
namespace std {
tempate<> void swap<MyClass>(MyClass& a, MyClass& b) {// throw()
a.Swap(b);
}
}
or
void swap(MyClass& a, MyClass& b) {// throw()
a.Swap(b);
}
|
이렇게 사용하는 이유는 객체의 특수성을 고려하여 만든 swap을 사용하는 것이 훨씬 효과적이기 때문임
예로 vector에 대한 swap은 vector::swap을 호출하도록 중복적재하는데 이유는 vector::swap이 vector 의 item의 복사본을 만들지 않기 때문이다.
댓글
댓글 쓰기