Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++ 복사-교환(Copy-and-Swap) 관용구 완벽 정리

복사-교환 관용구란 무엇인가?

객체의 대입(assignment) 작업은 크게 두 단계로 이루어집니다. 첫 번째는 객체가 가진 기존 상태를 해제하는 것이고, 두 번째는 그 자리에 새로운 상태를 구축하는 것입니다. 전자에는 소멸자(destructor)가 사용되고, 후자에는 복사 생성자(copy constructor)가 활용됩니다.

각각을 따로 구현하는 것은 비교적 간단하지만, 대입 연산자를 오버로딩할 때는 예외 안전성까지 고려해야 하므로 구현이 상당히 까다로워질 수 있습니다. 바로 이러한 어려움을 우아하게 해결해 주는 방법이 복사-교환(copy-and-swap) 관용구입니다.

동작 원리

복사-교환 관용구는 다음과 같은 순서로 동작합니다.

  • 1단계: 복사 생성자를 사용해 데이터의 지역(local) 복사본을 만듭니다.
  • 2단계: swap 함수를 호출하여 기존 데이터와 새로 만든 데이터를 맞바꿉니다.
  • 3단계: 임시 복사본은 스코프를 벗어날 때 소멸자에 의해 자동으로 파괴됩니다.

이 과정이 끝나면 객체에는 새 데이터의 복사본만 깔끔하게 남게 됩니다. 임시 객체의 소멸이 곧 기존 자원의 해제 역할을 겸하기 때문에, 코드가 단순해지고 강력한 예외 안전성(strong exception safety)까지 얻을 수 있다는 점이 이 관용구의 가장 큰 장점입니다.

관용구 구현에 필요한 세 가지 요소

복사-교환 관용구를 적용하려면 아래 세 가지가 반드시 준비되어 있어야 합니다.

  • 복사 생성자(copy constructor) — 원본 데이터의 복사본을 생성합니다.
  • 소멸자(destructor) — 임시 복사본이 담고 있던 기존 자원을 해제합니다.
  • swap 함수 — 두 객체의 멤버를 서로 교환합니다.

여기서 swap 함수는 클래스의 두 객체를 멤버 단위로 교환하는, 예외를 던지지 않는(no-throw) 함수여야 합니다. 예외가 발생하지 않기 때문에 교환 도중 객체가 불완전한 상태로 남는 일이 없습니다.

주의: std::swap을 그대로 쓰면 안 됩니다

std::swap은 내부적으로 복사 생성자와 대입 연산자를 사용하여 동작합니다. 따라서 대입 연산자 구현 안에서 std::swap을 호출하면 자기 자신을 다시 호출하는 무한 재귀에 빠질 위험이 있으며, 불필요한 복사로 인한 성능 저하도 발생할 수 있습니다.

그러므로 반드시 클래스의 멤버 변수들을 직접 교환하는 사용자 정의 swap 함수를 구현하고, 이를 복사-교환 관용구에서 활용해야 합니다.