7. Traps, Pitfalls, and Anti-Idioms

38. Object Identity

이는 필요한가?

T& T::operator=( const T& other ) 
{
  if( this != &other )    // the test in question
  {
    // ...
  }
  return *this;
}

복사 대입 연산자를 자가 대입 체크에 의존해야만 올바르게 동작하도록 하지 말라. 임시를 만들고 교환하는 관용구를 쓰는 것이 자가 대입에도 안전하고 강하게 예외에서 안전하다. 불필요한 작업을 피하는 최적화로 자가 대입 체크를 하는 것은 괜찮다.

문자열 리터럴에 대한 포인터를 비교하는 것은 정의되지 않은 행동이다. 일반적으로 포인터를 <, <=, >, >= 등으로 비교할 수 없다. std::less<>, std::greater<> 등은 순서를 잘 정의한다.

39. Automatic Conversions

std::string은 const char*로 묵시적 변환되지 않는다. 이는 합당한 이유가 있다. 묵시적 변환은 웬만하면 방지하는 것이 좋다. 변환 연산자를 쓰는 것을 피하라. 비-explicit 생성자를 피하라.

40. Object Lifetimes-Part 1

오브젝트는 언제 존재하는가?

void f() 
{
  T t(1);
  T& rt = t;
  //--- #1: do something with t or rt ---
  t.~T();
  new (&t) T(2);
  //--- #2: do something with t or rt ---
}// t is destroyed again

#2는 안전하지만 함수 전체적으론 안전하지 못하다. 이렇게 하지 않는 것이 좋다. 항상 예외 안전한 코드를 쓰도록 노력하라. 예외가 있더라도 자원이 안전히 해제되고 데이터가 일관적인 상태에 있도록 하라. 잘 쓰이지 않는 언어 특성을 피하라. 가장 효과적인 간단한 기법을 쓰라.

41. Object Lifetimes-Part 2

무엇이 잘못되었는가?

T& T::operator=( const T& other ) 
{
  if( this != &other )
  {
    this->~T();
    new (this) T(other);
  }
  return *this;
}

슬라이싱이 발생할 수 있고, 예외로부터 안전하지 않다. 잘 쓰이지 않는 언어 특성을 피하라. 가장 효과적인 간단한 기법을 쓰라. 불필요하게 간결하거나 똑똑한 코드를 피하라. 처음 쓸 때는 당신에게 분명해 보이더라도. 항상 예외 안전한 코드를 쓰도록 노력하라. 예외가 있더라도 자원이 안전히 해제되고 데이터가 일관적인 상태에 있도록 하라. 또한, 이는 오브젝트 생애 주기를 바꿀 수 있다. 파생 클래스도 깰 수 있다. 또한, 자가 대입 체크에 완벽히 의존한다. 복사 생성자를 구현할 때는 예외를 던지지 않는 swap을 제공하고 복사 대입 연산자를 이에 대해 구현하라. placement new 뒤에 명시적 소멸자를 통해 복사 대입 연산자를 복사 생성자를 통해 구현하는 짓은 절대 하지 말라.