반응형
0. 요약
- operator= 에서 자기대입에 대한 처리를 반드시 하자
- 일치성 검사를 통해 자기대입일 경우 처리하지 않도록 하는 방법
- 본래의 사본을 만들어 할당받는 방법
- 사본을 swap하는 방법
1. 자기 대입의 예시
- 자기 대입이란?
- 어떤 객체가 자기 자신에 대해 대입연산자를 적용하는 것
class widget{...};
Widget w;
w = w;
배열 또는 컨테이너 순환 중 자기 대입
- i와 j가 같다면 자기 대입
a[i] = a[j];
- 중복 참조로 인해 자기 대입 발생
- 중복 참조 : 여러 곳에서 하나의 객체를 참조하는 상태
- px와 py가 가리키는 대상이 같으면 자기 대입
*px = *py;
2. 자기 대입 문제
- 정상적인 경우
- 대입 연산 시 기존의 pb 포인터 삭제
- rhs의 pb포인터 값을 재할당 및 복사함
- 자기 대입일 경우
- 대입 연산시 기존의 pb포인터 삭제
- rhs의 pb포인터도 삭제되어 버림
- 대입 후에 비어 있는 Bitmap이 할당됨
class Bitmap{...};
class Widget{
public:
Widget& operator=(const Widget& rhs);
private:
Bitmap *pb;
};
Widget& Widget::operator=(const Widget& rhs){
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
3. 해결 방법
- 대입 연산자에서 일치성 검사 수행
- 자기 대입이 일어나는 경우는 극히 적음
- 하지만 대입할 때 마다 일치성 검사를 하므로 효율 떨어짐
- 또한 new 단계에서 예외 발생 시 삭제된 pb만 남음
Widget& Widget::operator=(const Widget& rhs){
if (this == &rhs) return *this; // 일치성 검사 수행
// 이후 과정 동일
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
- 삭제 전에 본래의 pb를 복제
- new 단계에서 예외 발생해도 pb 유지 가능
- 복제를 통해 자기 대입에 대한 방어도 가능함
Widget& Widget::operator=(const Widget& rhs){
Bitmap *pOrig = pb; // 원래 pb를 복제
pb = new Bitmap(*rhs.pb); // rhs의 pb를 대입
delete pOrig; // 원래 pb를 삭제
return *this;
}
- 복사 후 바꾸기 방법(copy and swap)
- 예외 안정성과 자기대입 안정성을 동시에 가진 operator= 구현 방법
- 29 항목에서 확인
- 방법 1과 같이 rhs 사본을 만들어 swap하는 방법
- 방법 2와 같이 값에 의한 전달 시 사본이 만들어지는 특징을 살려 바로 swap하는 방법
class Widget{
void swap(Widget& rhs);
};
// 방법 1.
Widget& Widget::operator=(const Widget& rhs){
Widget temp(rhs); // rhs 사본 만듬
swap(temp); // *this를 사본과 맞바꿈
return *this;
}
// 방법 2. 항목 20 참고
Widget& Widget::operator=(Widget rhs){
swap(rhs);
return *this;
}
참고
- Effective C++
반응형
'C++ > Effective C++' 카테고리의 다른 글
[Effective C++] 13. 자원 관리에는 객체가 그만 (0) | 2021.03.15 |
---|---|
[Effective C++] 12. 객체의 모든 부분을 빠짐없이 복사하자 (0) | 2021.03.15 |
[Effective C++] 10. 대입 연산자는 *this의 참조자를 반환하게 하자 (0) | 2021.03.15 |
[Effective C++] 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자 (0) | 2021.03.15 |
[Effective C++] 8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자 (0) | 2021.03.15 |