-
Effective C++...10일지 2022. 6. 9. 08:03
Operator=에서는 자기 대입에 대한 처리가 빠지지 않도록 하자
자기 대입이란?
어떤 객체가 자기 자신에 대해 대입 연산자를 적용하는 것을 말한다.
class Widget { ... };Widget w;
w = w;
// 같은 변수에 대입하는 경우
a[i] = a[j]; // 만약 i와 j가 같은 경우
*px = *py; // 자기 대입이 될 가능성이 높음
자기 대입이 문제가 되는 경우
포인터를 사용하고 있고 대입할 때 기존 포인터 값을 삭제하고 대입하는 값을 복사해 사용하는 코드가 있을 때, 자기 대입이 발생한 경우 제거된 값을 복사하려고 시도하게 될 수 있다.
// Widget에 Bitmap* pb가 있다.
Widget& Widget::operator=(const Widget& rhs)
{
delete pb;
pb = new Bitmap(*phs.pb); // 자기 대입인 경우 전 줄에서 pb가 삭제되었으므로 문제가 될 것이다.
return *this;
}
자기 대입을 막는 방법
이 것을 회피한 전통적인 해결 방법은 다음과 같이 첫 라인에 일치성 검사 코드를 추가하는 것이다.
Widget& Widget::operator=(const Widget& rhs)
{
if (this == &rhs) return *this; // 자기 대입인 경우 그대로 반환하고 종료한다.
delete pb;
pb = new Bitmap(*phs.pb);
return *this;
}
다른 방법으로는 문장을 조금 수정해서 안전하게 동작하도록 만들 수 있다.
Widget& Widget::operator=(const Widget& rhs)
{
Bitmap* pOrig = pb; // 기존의 값을 임시 변수에 저장해둔다.
pb = new Bitmap(*phs.pb); // 새로운! 값을 생성해 pb에 저장한다.
delete pOrig; // 안전하게 기존 값을 제거한다.
return *this;
}
또 다른 방법으로 복사 후 맞바꾸기라는 기법을 사용하면 도움이 된다.
class Widget {
void swap(Widget& rhs); // *this의 데이터와 rhs의 데이터를 맞바꾸는 함수.
}
Widget& Widget::operator=(const Widget& rhs)
{
Widget temp(rhs); // 임시 변수를 만들고
swap(temp); // 데이터를 서로 바꾼 뒤 함수를 빠져나가면
return *this; // 임시 변수가 삭제되면서 대입이 완료된다.
}
복사 후 맞바꾸기 기법은 C++의 특성을 이용해 다음과 같이 수정할 수 있다.
Widget& Widget::operator=(Widget rhs) // 값에 의한 참조로 인해 복사되어 온다.
{
swap(rhs); // 값으로 참조된 인자와 데이터를 서로 바꾼다.
return *this;
}
※ 값에 의한 참조로 제작한 경우 명확성이 떨어질 수 있으나 변수의 복사가 생성자단에서 일어나므로 조금 더 효율적일 수 있다.