티스토리 뷰

728x90

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

타이틀 입력부분C++ 연산자 operator= 에서는 자기대입에 대한 처리가 빠지지 않도록 하자.
자기대입(중복참조,자기참조)은 같은 객체를 참조 할 수 있는 위험이 있다.


자기참조,자기대입(Self Assignment)이란? 
객체가 자신에 대해 대입 연산자를 적용하는 것을 말합니다. 
ex)중복참조: 여러 곳에서 하나의 객체를 참조하는 상태

예제

1
2
3
4
5
6
Widget& Widget::operator=(const Widget& _rhs) 
    delete pb;                 //현재의 비트맵 사용을 중지합니다. 
    pb = new Bitmap(*_rhs.pb); //이제 rhs의 비트맵을 사용하도록만듭니다 
    return *this;              //*this == pb == &rhs.pb 즉,pb는 rhs의 Reference 
cs

위 코드의 문제점은 "operator=" 내부에서 *this(대입되는 대상)와 _rhs가 같은 객체일 가능성이 있습니다.
둘이 같은 객체면 delete 연산자가 *this 객체의 비트맵에만 적용되는 것이 아니라 _rhs의 객체까지 적용됩니다.
함수가 끝나버리는 시점이되면 자신의 포인터 멤버를 통해 물고 있던 객체가 이미 삭제된 상태되고, 소멸자에 의하여 Free 할 경우 Double Free가 되는 문제점이 발생 합니다. 

 

해법1: 일치성 검사(Identity Test)를 통해 자기대입을 점검한다.

1
2
3
4
5
6
7
8
9
10
Widget& Widget::operator=(const Widget& _rhs) 
    if(this == &_rhs)  //객체가 같은지, 즉 자기대입인지 검사합니다. 
    { 
        return *this;  //자기대입이면 아무것도 안 합니다. 
    } 
    delete pb; 
    pb = new Bitmap(*_rhs.pb); 
    return *this
}
cs

주의점: 예외 안전성에 문제가 있다. ‘new Bitmap’ 부분에서 예외가 발생하게 되면
(동적 할당에 필요한 메모리가 부족하다든지Bitmap 클래스 복사 생성자에서 예외를 던진다던지 해서) 


Widget 객체는 결국 삭제된 Bitmap을 가리키는 포인터를 껴안고 홀로 남고 맙니다.
그리고 *this와 rhs가 일치하는 경우는 희박합니다.
즉, 속도면에서 비효율적임에 틀림이 없고 위 코드도 완전한 해답이라고 할수 없습니다.

 

해법2: 예외 안전성에만 집중, 자기대입 문제는 무시.

즉, 문장 순서를 세심하게 바꾸는 것만으로 예외에 안전한(동시에 자기대입에 안전한) 코드가 만들어진다.

1
2
3
4
5
6
7
Widget& Widget::operator=(const Widget& rhs) 
    Bitmap *pOrig = pb;             //원래의 pb를 어딘가에 기억해 둡니다. 
    pb = new Bitmap(*rhs.pb);       //다음, pb가 *pb의 사본을 가리키게 만듭니다. 
    delete pOrig;                   //원래의 pb를 삭제합니다. 
    return *this
}
cs


장점:
1. new 에서 예외가 발생하더라도 delete가 실행되지 않으므로 Widget 객체가 원래 있던 상태로 남아있게 되고 
2. 자기 참조가 일어나더라도 안전한 동작이 이루어 집니다.
Ex)new연산자는 예외 발생시 bad_alloc 을 throw 합니다.(#include 필요)
이걸 이용해서 try~catch구문으로 에러를 잡는 방법도 가능합니다.

 

해법 3: 복사 후 맞바꾸기(copy and swap)기법을 이용한다.

1
2
3
4
5
6
7
8
9
10
11
12
class Widget 
public:  
    void swap(Widget& rhs);          //*this의 데이터 및 rhs의 데이터를 맞바꿉니다 
}; 
 
Widget& Widget::operator=(const Widget& rhs) 
    Widget temp(rhs);               //rhs의 데이터에 대해 사본을 하나 만듭니다. 
    Swap(temp);                     //*this의 데이터를 그 사본의 것과 맞바꿉니다. 
    return *this
}
cs

 

결론

1.operator= 를 구현할 때, 어떤 객체가 그 자신에 대입되는 경우를 제대로 처리하도록 만듭시다.
원본 객체와 복사대상 객체의 주소를 비교해도 되고,문장의 순서를 적절히 조정할 수도 있으며,
복사 후 맞바꾸기 기법을 써도 됩니다.


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

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함
250x250