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의 요구 조건 :
  1. T temp(a); 를 위한 복사 생성자 제공
  2. a = b; 를 위한 operator=() 재정의
  3. 예외 안정성을 고려 : 위는 예외에 전혀 안정적이지 않음
    1. 만약 a=b를 수행하고 b=temp 수행시 exception 이 발생할 경우 a만 변경된 상태에서 swap을 빠져나가는 경우 발생
    2. 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의 복사본을 만들지 않기 때문이다.

댓글

이 블로그의 인기 게시물

Raspberry pi 한글 설정 및 chromium 설치

Google Test를 이용한 Class의 Private/Protected Member Test