관리 메뉴

기억을 위한 기록들

[CPP-effective] 2-6 operator=에는 자기대입에 대한 처리를 빼먹지 말자. 본문

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

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