항목 5 : 사용자 정의 타입 변환 함수에 대한 주의를 놓지 말자

  • 사용자 타입에 암시적 타입 변환을 수행하기 위해 컴파일러가 사용하는 함수를 제공할지 말지 여부를 결정할 수 있다.
  • 컴파일러가 사용 가능한 타입 변환 함수
    1. 단일 인자 생성자
    2. 암시적 타입변환 연산자
  • 단일 인자 생성자 : 인자를 하나만 받아 호출하는 생성자
    • 두 가지 방법  »1. 매개변수를 하나만 받도록 선언  »2. 매개변수가 여럿이지만 처음 걸 제외한 나머지가 모두 기본값을 갖도록 선언
  • 암시적 타입 변환 연산자 : 타입 앞에 operator가 붙어있는 멤버함수. 반환값을 지정해 줄 수 없다.
    • ex)
      class Rational { 
          public: 
      	operator double() const; 
      }; 
      
  • 타입변환 함수들이 원하든 원하지 않든 호출되고 만다.
  • ex)
    Rational r(1, 2); 
    cout << r; 
    
  • 해석 순서
    1. Rational 객체에 operator« 함수가 없음
    2. operator double()를 통해 암시적 타입 변환
    3. double의 operator«이 호출된다.
  • 해결
    • operator double을 똑같은 일을 하되 다른 이름을 갖는 함수로 바꿔치기 한다.
    • 불편하지만 의도치 않은 잘못된 함수 호출을 막을 수 있다.
    • string 클래스도 char*로의 암시적 변환 연산자 대신 c_str 메소드를 제공한다.
  • 단일 인자 생성자를 통한 암시적 타입 변환은 더 까다롭다.
  • ex)
    template<class T> 
    class Array { 
    public: 
      Array(int size); // 이걸 통해 암시적 변환이 가능하다. 
    } 
    if(a == b[i]) ... // a도 b도 Array객체이고, 원래라면 a[i] == b[i]여야 하지만 실수로 이렇게 작성했을 시. 
    
  • 해석 순서
    1. b[i]가 단일 생성자로 인해 암시적 타입 변환된다.(static_cast<Array>(b[i])의 효과)
    2. 단일 인자 생성자를 진짜로 제공해야 할 경우가 많으므로 피하기가 어렵다.
  • 해결
    • explicit 키워드 사용 : 암시적 타입 변환을 막는다.
    • ‘사용자 정의 타입 변환 함수는 두개 이상 동시에 쓰이지 않는다’ 규칙을 사용한다.
    • ex) Array 템플릿에서 단일 생성자가 받는 int 인자를 대체할 ArraySize란 클래스를 제공한다. -> if(a == b[i])에서 int -> ArraySize -> Array 두 번의 타입 변환을 거쳐야 하기 때문에 에러가 발생하게 된다.
  • Array a(10)에서 10은 ArraySize의 단일 생성자로 타입 변환 되므로 성공한다.
  • ArraySize는 Array에서만 사용되므로 내부 클래스로 선언한다. 다른 사람이 쓸 수 있게 public으로 선언.
  • 프록시 클래스 : 다른 객체를 대신하는 클래스(30)
  • ex) ArraySize는 int를 대신한다.
    • 다른 방법으로는 어찌할 수 없는 소프트웨어 동작식을 조정하는 데 요긴하다.
  • 컴파일러가 맘대로 암시적 타입 변환을 하게 되면 득보다는 실이 많다.
  • 타입 변환 연산자(함수)는 마음 깊은 곳에서 원하지 않는 이상 제공하지 말도록 하자!

항목 6 : 증가 및 감소 연산자의 전위 / 후위 형태를 반드시 구분하자

  • 후위 형태는 int 타입의 인자를 받도록 하고 컴파일러는 후위 형태 증감 연산자가 호출될 때 0을 넘기도록 구현한다.
    • 전위 / 후위를 구분하기 위함이다.
  • 전위 증감 연산자 : 참조자 타입 리턴
  • 후위 증감 연산자 : const 객체 타입 리턴
  • 후위의 int 타입 매개변수는 내부적으로 사용되지 않는다.(전위와 후위의 구분용이다.)
  • 후위 연산자가 const가 아닌 객체를 반환하길 꺼리는 이유
    1. 기본 제공 타입에 대한 증가 연산자 동작과 맞지 않는다.
      • ex) int i; i++++; // 에러!
    2. 직관적이지 않고 헷깔린다.(사용자 편의를 위해)
      • ex) i++++;가 허용이 되면 i는 한번만 증가한다.(후위 연산자는 임시 정수를 반환하기 때문이다.)
  • 후위 증감 연산자는 반환값으로 임시 객체를 만들어야 하므로 사용자 정의 타입을 가지고 작업할 때는 되도록 전위 증감 연산자가 좋다.
  • 후위 증감 연산자는 반드시 전위 증감 연산자를 사용하여 구현할 것!
    • 코드 유지(후위든 전위든 동작 방식이 같아야 함을 보장)가 편하기 때문.