operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자.


class widget{ ... }


widget w;

w=w;


widget w[10];

w[i] = w[j];


*pw1 = *pw2;

위의 경우 자기대입의 소지가 다분하다.


같은 타입으로 만들어진 객체 여러 개를

참조자 혹은 포인터로 물어 놓고 동작하는

코드를 작성할 때는 같은 객체가 사용 될

가능성을 고려하는 것이 바람직하다.




파생 클래스 타입의 객체를 참조하거나 가리키는 용도로

기본 클래스의 참조자나 포인터를 사용할 수도 있다.


class Base { ... };

class Derived : public Base { ... }

void dosomethig ( const Base& rb, Derived* pd );

위의 경우에도 자기대입 연산의 위험이 다분하다.


일반적인 operator= 구현을 보겠다.

위 코드의 문제점은 *this와 rhs가

같은 객체일 경우 문제점이 발생한다.

첫 줄에서 이미 지워진 메모리 영역을

두번 째 줄에서 다시 가리키고 있다.

또한 예외에도 안전하지 않다는 점.




자 이제 이 두문제를 해결해보자.


1. 일치성 검사


위의 operator= 구현 맨 윗줄에

다음과 같은 코드를 삽입합니다.

if(this == &rhs) return *this;

자기 대입인지 확인한 후, 자기 대입이면

아무것도 하지 않고 자기 자신을 리턴한다.




자기 대입의 위험성은 이제 완전히

사라진 듯 보이지만 여전히

예외에 안전하지 않다는 문제점이 있다.

또한 대입을 할 때마다 검사를

해줘야 한다는 비용문제도 생긴다.




'new bitmap' 이 부분에서

동적 할당에 필요한 메모리가 부족하다든지

bitmap 클래스 복사 생성자에서 예외를

던진다든지 해서 메모리 할당이 제대로

진행되지 않을 가능성이 있다.


2. 문장의 순서를 바꿔보자


'new bitmap' 부분에서 예외가 발생하더라도

pb는 원래의 데이터를 가지고 있다.

'new' 연산이 정상적으로 수행되면

그 때 pOrig를 삭제한다.

(pOrig는 pb의 원래 가리키던 곳을 가리키고 있다.)




위 코드는 자기대입 현상 또한 완벽히 처리한다.


그림이 좀 엉성하지만

코드를 보고 조금만 생각하면

충분히 이해가 될 것이다.




예외 안전성과 자기대입 안전성을 동시에

가진 operator=을 구현하는 방법으로

복사 후 맞바꾸기라는 기법이 있는데

이 기법은 책의 중반부에 자세히

설명하고 있기 때문에

다음 포스팅에서 다루도록 하겠다.


POINT!!

1. operator=을 구현할 때, 어떤 객체가 그 자신에

대입되는  경우를 제대로 처리하도록 만들어야 한다.


1) 일치성 검사

2) 문장의 순서 조정

3) 복사 후 맞바꾸기


2. 두 개 이상의 객체에 대해 동작하는 함수가 있다면,

이 함수에 넘겨지는 객체들이 같은 경우에도

정확하게 동작하는지 확인해 봐야한다.


+ Recent posts