본문 바로가기

Effective C++ 정리

11. operator = 자기 대입 처리하기

class Wiget {...};

Wiget w;

...

w = w;

 

위와 같은 소스 코드는 자기 대입이 이루어지고 있는 것을 쉽게 확인할 수 있네요.

그러나 반복문에서 이루어지는 객체의 자기 대입은 언제 놓칠 수도 있기 때문에

일치성 검사, 복사 대입 후 삭제, 복사 후 맞바꾸기 방법을 활용할 수 있습니다.

 

class Bitmap { ... };

class Widget
{
public:
	...

private:
	// 힙에 할당하기 위한 멤버
	Bitmap*	m_pBitmap;

public:
	// 안전하지 않은 operator = 연산자
	Widget& operator = (const Widget& rhs)
	{
		// 기존에 할당되어 있는 것을 지운다.
		delete m_pBitmap;

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

 

위와 같은 operator = 자기 대입 연산자 오버로딩에서 rhs 인자로

메모리에서 제거 된 rhs.m_pBitmap가 들어오면 문제가 발생할 수 있습니다.

먼저 전통적인 방법인 함수의 첫머리에 쓰는 일치성 검사를 살펴보겠습니다.

 

일치성 검사 

// 여전히 안전하지 않은 operator = 연산자
Widget& operator = (const Widget& rhs)
{
	// 자기대입을 시도할 때 그대로 리턴한다.
	if (this == &rhs) return *this;

	// 기존에 할당되어 있는 것을 지운다.
	delete m_pBitmap;

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

 

먼저 객체가 동일한지 검사합니다. 동일하지 않다면 복사 생성을 진행합니다.

그러나 Bitmap의 복사 생성자가 예외를 던지게 되면 삭제되어 있는 객체를 가리킬 수 있는 문제점이 존재합니다.

 

복사 대입 후 삭제

// 복사 대입 후 삭제
Widget& operator = (const Widget& rhs)
{
	// 원래의 Bitmap 객체를 다른 곳에 복사해둔다.
	Bitmap* pOriginBitmap = m_pBitmap;

	// 새로운 사본을 생성하고 가리킨다.
	m_pBitmap = new Bitmap(*rhs.m_pBitmap);
    
	// ... 예외를 대처한다 ... //

	// 원래의 Bitmap 객체를 제거한다.
	delete pOriginBitmap;

	return *this;
}

 

미리 사본을 들고 있기 때문에 복사 생성자 예외로 던져진 delete된 문제를 해결할 수 있게 되었습니다.

더 나아가 복사 후 맞바꾸기는 일치성 검사의 수고를 덜어줄 수 있습니다.

 

복사 후 맞바꾸기

class Bitmap { ... };

class Widget
{
	...

	// *this의 데이터와 rhs의 데이터를 바꾼다.
	void swap(Widget& rhs);

	...

	// 복사 후 맞바꾸기
	Widget& operator = (const Widget& rhs)
	{
		// rhs의 데이터에 대한 사본을 생성
		Widget temp(rhs);

		// *this의 데이터를 사본의 데이터와 교환
		swap(rhs);

		// ... 예외를 대처한다 ... //

		return *this;
	}
};