728x90
반응형

자기 대입(self assignment)

어떤 객체가 자기 자신에 대해 대입 연산자를 적용하는 것을 뜻한다.

class Widget {...};

Widget w;
w = w; // 자기에 대한 대입

자기 대입이 생기는 이유는 여러 곳에서 하나의 객체를 참조하는 상태(중복 참조)가 일어나기 때문인데, 따라서 같은 클래스의 객체를 다룰 때는 중복 참조를 고려하여 설계해야 한다.

 

그리고 이때 조심해야 하는 것이 있는데 밑의 코드를 봐보자.

class Bitmap {...};
class Widget
{
private:
	Bitmap *pb; // 힙에 할당한 객체를 가리키는 포인터
}

Widget& Widget::operator=(const Widget& rhs) // 안전하지 않게 구현된 operator=
{
	delete pb; // 현재의 비트맵 사용을 중지
	pb = new Bitmap(&rhs.pb); // rhs의 비트맵을 사용하도록 함
	return *this; // 이 부분에 대해서는 항목 10 참고
}

여기서 만약 rhs와 this가 같은 주소면 this에서만 적용될 일이 rhs에서도 같이 적용될 것이다. 즉, delete pb 실행 시 rhs의 pb도 삭제되며 제대로 된 동작을 할 수 없을 것이다.

 

일치성 검사를 통한 문제 해결

Widget& Widget::operator=(const Widget& rhs)
{
	if(this == &rhs)
		return *this; // 자기 대입 검사

	delete pb;
	pb = new Bitmap(*rhs.pb);
	return *this;
}

전통적인 방법으로 operator=의 첫마디에서 본인과 일치하는지 검사하는 것이다.

하지만 예외 안정성에는 문제가 있는데, 'new Bitmap' 표현식에서 예외가 터지면 이미 삭제된 비트맵을 가리키는 포인터를 가지게 될 것이고, *this와 rhs 비교 검사를 거쳐야 하므로 속도 면에서도 비효율적이다.

 

코드 순서 변경을 통한 예외 안정성 문제 해결

Widget& Widget::operator=(const Widget& rhs)
{
	Bitmap *pOrig = pb; // 원래의 pb를 기억
	pb = new Bitmap(*rhs.pb); // pb가 *pb의 사본을 가리키게 함
	delete pOrig; // 원래의 pb를 삭제함
    
	return *this;
}

이렇게 순서를 변경하게 되면 'new Bitmap'부분에서 예외가 발생하더라도 delete가 실행되지 않으므로 pb는 변경되지 않고, 자기 참조가 일어나더라도 안전한 동작이 이루어진다.

 

복사 후 맞바꾸기 기법을 이용한 문제 해결

class Widget 
{ 
public:  
    void swap(Widget& rhs);          // *this의 데이터 및 rhs의 데이터를 바꾼다
}; 
 
Widget& Widget::operator=(const Widget& rhs) 
{ 
    Widget temp(rhs);               // rhs의 데이터에 대해 사본을 만듬
    Swap(temp);                     // *this의 데이터를 그 사본의 것으로 교체
    return *this; 
}

이 방법은 C++의 아래의 두 가지 특징을 활용해서 조금 다르게 구현할 수도 있다.

  • 클래스의 복사 대입 연산자는 인자를 값으로 취하도록 선언하는 것이 가능하다.
  • 값에 의한 전달을 수행하면 전달된 대상의 사본이 생긴다.
Widget& Widget::operator=(Widget rhs) // 값 전달
{
	swap(rhs);
	return *this;
}

 

요약

  • operator=을 구현할 때, 어떤 객체가 그 자신에 대입되는 경우를 제대로 처리하도록 만들자. 원본 객체와 복사 대상 객체의 주소를 비교해도 되고, 문장의 순서를 적절히 조절할 수도 있으며, 복사 후 맞바꾸기 기법을 써도 된다.
  • 두 개 이상의 객체에 대해 동작하는 함수가 있다면, 이 함수에 넘겨지는 객체들이 사실 같은 객체인 경우에 정확하게 동작하는지 확인하자.
728x90
반응형