[C++] 항목 11: operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자
카테고리: Cpp
태그: Cpp
이 글은 아래의 책을 자세히 정리한 후, 정리한 글을 GPT에게 요약을 요청하여 작성되었습니다.
이펙티브 C++ 제3판, 스콧 마이어스 저자, 곽용재 번역
📦 2. 생성자, 소멸자 및 대입 연산자
👉🏻 항목 11: operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자
w = w
,a[i] = a[j]
,*px = *py
같은 코드에서 동일한 객체에 대한 대입이 일어날 수 있음.- 겉보기엔 문제 없어 보여도, 자원(resource)을 관리하는 객체라면 자기대입 시 심각한 오류 발생 가능.
⚠️ 문제되는 코드 예시
Widget& Widget::operator=(const Widget& rhs) {
delete pb;
pb = new Bitmap(*rhs.pb); // 예외 발생 가능
return *this;
}
*this == rhs
인 경우 →pb
삭제 후 복사 → 예외 발생 시 dangling pointer (삭제된 메모리 가리킴)- 자기대입도 위험, 예외 발생도 위험
✅ 해결 방법 1: 문장 순서 조정
Widget& Widget::operator=(const Widget& rhs) {
Bitmap* pOrig = pb;
pb = new Bitmap(*rhs.pb);
delete pOrig;
return *this;
}
- 먼저 복사, 그 후 삭제 → 예외가 발생해도 기존 상태 유지됨
- 자기대입이든 아니든, 예외가 나든 안 나든 안전하게 동작
❓ 자기대입 체크는 필요 없을까?
if (this == &rhs) return *this;
- 드물게 발생하는 자기대입만 막기 위해 불필요한 분기, 코드 복잡도, 성능 저하 발생
- 문장 순서 조정으로 안전하게 만들면, 체크 안 해도 OK
🔁 해결 방법 2: 복사 후 맞바꾸기 (Copy-and-Swap)
개념 설명
-
먼저 매개변수 복사본을 만들고, 그 복사본과 현재 객체의 내부 자원을 맞바꾼다(swap)
-
swap
함수는 두 객체의 자원을 빠르게 맞바꾸는 함수 (예: 포인터 변수 2개를 단순히 바꾸는 것)
void swap(Widget& a, Widget& b) {
std::swap(a.pb, b.pb); // std::swap을 써서 내부 포인터만 바꿈
}
방법 1: 명시적 복사 + swap
Widget& Widget::operator=(const Widget& rhs) {
Widget temp(rhs); // 복사 생성자로 사본 생성
swap(*this, temp); // *this와 temp의 내부 데이터를 맞바꿈
return *this;
}
방법 2: 값 전달로 암시적 복사 + swap
Widget& Widget::operator=(Widget rhs) { // rhs는 이미 복사본
swap(*this, rhs); // *this와 rhs를 맞바꿈
return *this;
}
- 이 방식은 복사 생성자만 잘 구현돼 있으면 자기대입 + 예외 안전성을 자동으로 확보할 수 있음
🧐 정리
-
operator=
에서는 자기대입과 예외 모두 꼭 고려해야 한다. -
해결 방법 3가지:
- 일치성 테스트:
if (this == &rhs)
→ 단순하지만 덜 우아하고 성능 손해 있음 - 문장 순서 조정: → 먼저 복사, 그다음 삭제 → 안전하고 효율적
- 복사 후 맞바꾸기 (Copy-and-Swap):
→ 복사본과
*this
의 자원을swap
함수로 교환 → 복사 생성자와 swap만 잘 있으면 매우 깔끔하고 안전한 방식
- 일치성 테스트:
-
특히 자원(Resource)을 다루는 클래스는 자기대입과 예외 안전성을 항상 고려한
operator=
가 필요함. -
또한 두 객체가 실은 같은 객체일 수도 있다는 가능성을 잊지 말자.
댓글남기기