Study/Effective C++

[effective C++] Chapter 02 : 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자

코딩뚜벅이 2023. 8. 3. 22:58

11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자

  • 자기 대입
  • operator= 자기 참조 에러 대책
  • 이것만은 잊지 말자 !

자기 대입

 

 

 위와 같은 코드는 일반적이지 않다. 하지만 문법상 문제가 있지도 않다. 그나마 위는 조금 눈에 띄는 형태의 자기 대입이 이루어지고 있지만 아래의 코드를 확인해보자.

 

 

 첫번째 문장에서 i와 j는 같은 값일 가능성을 가지고 있고 두번째 문장의 x와 y 역시 같을 가능성을 가지고 있다. 즉, 자기 대입의 가능성을 가지고 있지만 많은 코드 속에서 위와 같은 코드를 발견하기란 쉽지 않다.

 

 우리는 자원 관리 용도로 객체를 생성해야하고 이러한 객체들이 복사될 때 잘 동작하도록 해야하는데 유의해야하는 것이 대입 연산자이며 여러 곳에서 하나의 객체를 참조하는 중복 참조(aliasing) 또한 조심해야 한다. 따라서 우리는 같은 타입으로 만들어진 객체를 여러 참조자 혹은 객체가 바라보고 동작하는 가능성을 염두해두고 고려해야한다. 

 

 

 위 코드는 동적 할당된 비트맵을 가리키는 원시 포인터를 데이터 멤버로 갖는 클래스이다. 그리고 operator=의 코드가 구현되어 있는데 operator= 내부에서 *this와 rhs가 같은 객체일 가능이 있다는 문제점이 있다. 만약 같은 객체라면 delete 연산이 *this 객체의 비트맵에만 적용되는 것이 아닌 rhs 객체까지 적용되어 버린다. 함수가 끝나는 시점에는 Widget 객체가 포인터 멤버를 통해 물고 있던 객체가 삭제되는 상황의 발생을 야기한다. 

 


 

operator= 자기 참조 에러 대책

 

 

 자기 참조에 있어서 전통적인 방법 대처는 일치성 검사를 하는 것이다. if문을 통해 객체가 자기대입인지 아닌지, 혹시 자기 대입이라면 아무것도 하지 않고 반환하게끔 구현하는 것이다. 하지만 만약 예외가 발생해서 일치성 검사를 통과하게 되면 Widget 객체는 삭제된 Bitmap을 가리키는 죽은 포인터를 가지고 남게 된다. 따라서 더 나은 해결 방법은 아래와 같다.

 

 

 

 위 코드는 원래의 pb가 가리키는 포인터가 가리키는 객체를 다른 객체에 복사하고 new를 통해서 포인터의 사본을 가리키게 만든 다음, 원래의 pb를 삭제하는 방법을 보여주고 있다. new 부분에서 예외가 발생하더라도 pb는 변경되지 않은 상태를 유지할 수 있게 된다. 앞서 설명한 전통적인 방법에 있어서 문제는 예외 부분이었으므로 예외를 해결한 위의 방법은 효율적인 방법이라고 할 수는 없겠지만 안정성에 있어서는 옳다라고 말할 수 있다.

 

  또다른 방법은 복사 후 맞바꾸기(copy and swap) 기법을 이용하는 것이다.

 

 

 위의 코드에서 swap()은 *this와 인자의 값을 맞바꾸고 temp()는 인자의 데이터에 대해 사본을 하나 만들어 준다. 지금은 operator= 작성에 많이 쓰이는 위 기법이 어떠한 형태로 이루어져있는지만 보고 넘어가도록 하겠다. 

 위 방법은 클래스의 복사 대입 연산자는 인자를 값으로 취하도록 선언하는 것이 가능하다는 점과 값에 의한 전달을 수행하면 전달된 대상의 사본이 생긴다는 C++의 특징을 이용하여 아래와 같이 구현할 수도 있다.

 

 

 

 operator=에 의해 넘어온 인자는 값에 의한 전달이므로 기존 객체의 사본이 전달되게 되고, *this를 이 사본의 데이터와 맞바꾸는 형태로도 사용할 수 있다.

 


 

이것은 잊지 말자 !

1. operator-을 구현할 때, 어떤 객체가 그 자신에 대입되는 경우를 제대로 처리하도록 만들자. 원본 객체와 복사 대상 객체의 주소를 비교해도 되고, 문장의 순서를 적절히 조절할 수도 있으며, 복사 후 맞바꾸기 기법을 사용해도 된다.

 

2. 두 개 이상의 객체에 대해 동작하는 함수가 있다면, 이 함수에 넘겨지는 객체들이 사실 같은 객체인 경우에 정확하게 동작하는지 확인해라.