Post

[ STUDY/Effective C++ ] 2022. 8. 31. 19:03

 

Widget w;
w= w; //자기대입
a[i] = a[j]; //자기대입 가능성이 큼

중복참조로 인해 자기대입이 발생하는경우들이 있다. 자기대입이 발생하게되면 자원관리를 제대로 못할 수 있고 자원관리는 곧 프로그램 자체와 밀접하게 관련이 있다.

 

 

Widget&
Widget::operator=(const Widget& rhs)
{
    delete pb; // 현재 비트맵 사용을 중지
    pb = new Bitmap(*rhs.pb); //rhs비트맵을 사용하도록 함
    
    return *this;
}

여기서 *this와 rhs가 같은 객체일 가능성이 있다. 만약 그렇다면 rhs의 객체까지 delete가 적용되어 해당 Widget객체는 자신의 포인터멤버를 통해 가리키던 객체가 삭제되는 상태가 발생하게된다.

 

 

 

Widget&
Widget::operator=(const Widget& rhs)
{
    if(this==&rhs) return *this;

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

이를 해결하기위해서는 if문을 통해 일치성 검사를 하여 자기대입을 점검하는 방법이 있다. 

하지만 이또한 자기대입에 대한 문제점만 해결할 뿐, 만약 자기대입이 아니라서 다음 단계로 갔는데

new Bitmap에서 예외가 터지는 경우는 아직 해결이 되지 않았다.

 

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

코드의 순서를 바꿔주기만 했을뿐인데도, 이제는 new Bitmap부분에서 예외가 발생하더라도 pb는 변경되지않은 상태가 유지되기때문에 예외에서 안전하다.

그리고 일치성 검사를 하는 if문을 없애도 괜찮은 이유는 operator=을 예외에 안전하게 구현하면 대부분이 자기대입에도 안전한 코드가 나온다. 또 일치성 검사를 하는만큼 코드가 커지는데다 실행 시간 속력이 줄어들 수 있으며 등등의 비효율적인 모습이 결과적으로 나타날 수 있으니 되도록 피할 수 있으면 하지않는것이 좋다.

 

class Widget {
	...
	void swap(Widget& rhs);  //*this의 데이터 및 rhs의 데이터를 맞바꿈
	...
};

Widget& Widget::operator=(const Widget& rhs)
{
	Widget temp(rhs);  // rhs의 데이터에 대한 사본을 만듬 (값에 의한 전달을 이용하면 사본생김)
	swap(temp);  // *this의 데이터를 그 사본의 것과 맞바꿈
	return *this;
}
/*위의 코드를 좀 더 깔끔하게*/
Widget& Widget::operator=(const Widget rhs) //값에 의한 전달에 의해 매개변수로 사본생성!
{
	
	swap(rhs);  // *this의 데이터를 사본의 것과 맞바꿈
	return *this;
}

 

예외안정성과 자기대입 안정성을 동시애 가진 operator=을 구현하는 또 다른 방법으로는 '복사 후 맞바꾸기'가 있다.

swap함수를 이용하는데, 여기서 쓰인 swap함수는 클래스가 기존에 가지고있던 포인터변수를 매개변수로 받은 변수의 포인터와 맞바꾼다는 의미인것같다. 

그래서 이를 이용하여 사본과 맞바꾸게되면 opwerator=을 자기대입과 예외에 대해서 안정성을 가지고 구현할 수 있다.

여기에 대한 자세한 설명은 항목 29에서 더 확인해볼 수있다.

 

 

▲ top