C & CPP/Effective C++
[CPP-effective] 2-6 operator=에는 자기대입에 대한 처리를 빼먹지 말자.
에드윈H
2021. 3. 30. 13:38
자기대입(self assignment)란, 어떤 객체가 자기 자신에 대해 대입 연산자를 적용하는 것을 말합니다.
class Widget{...};
Widget myWidget;
myWidget=myWidget;
보통 위와같은 식을 말하는데 이외에도
a[i]=a[j]; //i와 j가 같을수도 있다.
*px=*py; //자기대입 가능성이 아주 높다,
px와 py가 가리키는 대상이 같아지면 여러곳에서 하나의 객체를 참조하는상태 즉 중복참조(aliasing)이라고 불린다.
이외에도 자식부모 클래스의 상속관계 있는 클래스 사이에서도 중복 참조는 일어 날수도 있다.
class Base{..};
class Player : public Base {...};
void DoSomething(const Base& rb, Player* p1); //rb와 *p1은 같은 객체 였을수도 있다.
이런 자기 대입이 일어날때 중복참조라던가 자원 관리 객체들이 복사될 때 잘 작동해야하는데, 그 중 조심해야 할것이 바로, 대입 연사자 operator= 이다.
일단 안전하지 않은 대입연산자를 먼저 보자.
class Player{
///..
Player& operator=(const Player& ref);
private:
char* mPlayerName;
};
Player& Player::operator=(const Player& ref) //불안전하다.
{
delete mPlayerName;
mPlayerName = new char(*ref.mPlayerName);
return *this;
}
여기서 생기는 자기 참조 문제는 operator= 내부에서 대입되는 대상과 ref 객체가 같은 객체일 가능성이 있다는 것이다.
이것을 해소해줄게 전통적인 방벙니 일치성 검사(identity test)를 통해 자기대입을 점검하는 것이다.
class Player{
///..
Player& operator=(const Player& ref);
private:
char* mPlayerName;
};
Player& Player::operator=(const Player& ref)
{
if(this == &ref) //일치성 검사
return *this;
delete mPlayerName;
mPlayerName = new char(*ref.mPlayerName);
return *this;
}
이렇게 자기대입인지 검사하는 방법이외에도 추가로 '복사 후 맞 바꾸기(copy and swap)'이라고 알려진 기법도 간략히 설명하자면,
class Player{
///..
void swap(Player& ref);
Player& operator=(const Player& ref);
private:
char* mPlayerName;
};
Player& Player::operator=(const Player& ref)
{
Player tempPlayer(ref); //ref의 데이터에 대해 사본을 만든다.
swap(tempPlayer); //*this의 데이터와 그 사본과 서로 맞바꾼다.
return *this;
}
정리
* operator=을 구현할 때, 어떤 객체가 그 자신에 대입되는 경우를 제대로 처리하도록 만들자. 원본 객체와 복사대상 객체의 주소를 비교해도 되고, 문장이 순서를 적절히 조절할 수도 있으며, 복사 후 맞바꾸기 기법을 써도 된다.
* 두 개 이상의 객체에 대해 동작하는 함수가 있다면, 이 함수에 넘겨지는 객체들이 사실 같은 객체인 경우에 정확하게 동작하는지 확인해보자.