1. 자기 대입이란?
class Widget{...};
Widget w;
w =w;
이런 경우는 빈번하게 일어난다.
a[i] = a[j] ;
*px = *py;
위와 같은 경우는 실수하기가 쉽다! 보통, 이러한 자기 대입이 생기는 이유는 여러 곳에서 하나의 객체를 참조하는 상태, 다시 말해 중복 참조라고 불리는 것 때문이다. 같은 타입으로 만들어진 객체 여러 개를 참조자 혹은 포인터로 물어 놓고 동작하는 코드를 작성할 때는 같은 객체가 사용 될 가능성을 고려하는 것이 일반적으로 바람직한 자세이다.
2. 대입 연산자와 자기 대입
Widget& Widget::operator = (const Widget& rhs)
{
delete pb;
pb = new Bitmap(*rhs.pb);
return *pb;
}
*this와 rhs가 같은 객체일 가능성이 있다. 이 둘이 같은 객체이면, delete 연산자가 *this 객체의 비트맵에만 적용되는 것이 아니라 rhs의 객체까지 적용되어 버린다.
3. 해결법 3가지
- 일치성 검사
Widget& Widget::operator = (const Widget& rhs)
{
if(this == &rhs) return *this;
delete pb;
pb = new Bitmap(*rhs.pb);
return *pb;
}
단점으로 분기에 의한 비용 문제가 있다. 개인적으로 가장 간단하기 때문에 적극 추천한다. 홍정모 교수님(지금은 아니시지만..)의 c++ 강의에서도 이처럼 일치성 검사를 통해 중복 참조를 피하였다.
그리고, 만약 Bitmap 생성중 예외 사항이 발생한 경우 원본인 pb의 메모리도 해제되는 문제가 있다.
- 코드 순서 바꾸기
Widget& Widget::operator = (const Widget& rhs)
{
Bitmap* pOrig = pb;
pb = new Bitmap(*rhs.pb);
delete pOrig;
return *this;
}
자기 대입이 일어나는 경우를 적다 가정할 때 가장 좋은 방법이다. 그리고 원본 객체를 복사해두고 예외검사를 하기에 실패 했을 경우에 대한 안정성이 보장된다.
- 복사 후 맞바꾸기
class Widget {
void swap(Widget& rhs);
};
Widget& Widget::operator= (const Widget& rhs)
{
Widget temp(rhs);
swap(temp);
return *this;
}
이 방법은 간단히 말하면 복사 대상의 사본을 만들어 놓은 후 모든 작업을 완료 하고 예외가 발생하지 않으면 swap 함수를 통해 사본과 원본을 교체하는 방식이다. 예외가 던져져도 원본 자체는 손상되지 않기때문에 예외안정성이 보장된다.
이것만은 잊지 말자!
- operator=을 구현할 때, 어떤 객체가 그 자신에 대입되는 경우를 제대로 처리하도록 만듭시다. 원본 객체와 복사대상 객체의 주소를 비교해도 되고, 문장의 순서를 적절히 조정할 수도 있으며, 복사 후 맞바꾸기 기법을 써도 됩니다.
- 두 개 이상의 객체에 대해 동작하는 함수가 있다면, 이 함수에 넘겨지는 객체들이 사실 같은 객체인 경우에 정확하게 동작하는지 확인하세요.
'C++ > Effective C++' 카테고리의 다른 글
[Effective C++] 13. 자원 관리에는 객체가 그만! (0) | 2020.08.03 |
---|---|
[Effective C++] 12. 객체의 모든 부분을 빠짐없이 복사하자 (0) | 2020.07.28 |
[Effective C++] 10. 대입 연산자는 *this의 참조자를 반환하게 하자 (0) | 2020.07.28 |
[Effective C++] 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자 (0) | 2020.07.28 |
[Effective C++] 8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자 (0) | 2020.07.27 |