항목 29 : 예외 안전성이 확보되는 그날 위해 싸우고 또 싸우자!
예외 안전성을 확보하려면 두 가지의 요구사항을 맞추어야 합니다.
1. 자원이 새도록 만들지 않습니다.
2. 자료구조가 더럽혀지는 것을 허용하지 않습니다.
예외 안전성을 갖춘 함수는 세 가지 보장중 하나를 제공합니다
1. 기본적인 보장 : 함수 동작 중에 예외가 발생하면, 실행 중인 프로그램에 관련된 모든 것들을 유효한 상태로 유지하겠 다는 보장입니다. 어떤 객체나 자료구조도 더럽혀지지 않으며, 모든 객체의 상태는 내부적으로 일관성을 유지하고 있습니다(즉, 모든 클래스 불변속성이 만족된 상태입니다).
2. 강력한 보장 : 함수 동작 중에 예외가 발생하면, 프로그램의 상태를 절대로 변경하지 않겠다는 보장입니다. 이런 함수를 호출하는 것은 원자적인 동작이라고 할 수 있습니다. 호출이 성공하면(예외가 발생하지 않으면) 마무리까지 완벽하게 성공하고, 호출이 실패하면 함수 호출이 없었던 것처럼 프로그램의 상태가 되돌아간다는 면에서 말이죠.
3.예외불가 보장 : 예외를 절대로 던지지 않겠다는 보장입니다. 약속한 동작은 언제나 끝까지 완수하는 함수라는 뜻이죠. 기본제공 타입(int, 포인터 등)에 대한 모든 연산은 예외를 던지지 않게 되어 있습니다.
아무 보장도 제공하지 않으면 예외에 안전한 함수가 아니므로 '어떤 보장을 제공할 것인가'에 대한 선택을 해야 합니다.
예외에 속수무책인 함수를 탈바꿈시켜 강력한 예외 안전성 보장을 제공하는 함수로 거듭나게 만드는 일반적인 설계 전략을 하나 알려드리겠습니다.
이 전략은 '복사-후-맞바꾸기'라는 이름으로 알려져 있는데, 무척 간단합니다. 어떤 객체를 수정하고 싶으면 그 객체의 사본을 하나 만들어 놓고 그 사본을 수정하는 것입니다. 이렇게 하면 수정 동작 중에 실행되는 연산에서 예외가 던져지더라도 원본 객체는 바뀌지 않은 채로 남는 거죠. 필요한 동작이 전부 성공적으로 완료되고 나면 수정된 객체를 원본 객체와 맞바꾸는데, 이 작업을 '예외를 던지지 않는' 연산 내부에서 수행합니다.
이 전략은 대개 '진짜' 객체의 모든 데이터를 별도의 구현 객체에 넣어두고, 그 구현 객체를 가리키는 포인터를 진짜 객체가 물고 있게 하는 식으로 구현합니다.
어떤 함수가 제공하는 예외 안정성 보장의 강도는, 그 함수가 내부적으로 호출하는 함수가 제공하는 가장 약한 보장을 넘을 수 없다는 사실도 잘 기억하셔야 합니다!
꼭 잊지 말아야 할 것!
1. 예외 안전성을 갖춘 함수는 실행 중 예외가 발생되더라도 자원을 누출시키지 않으며 자료구조를 더럽힌 채로 내버려 두지 않습니다. 이런 함수들이 제공할 수 있는 예외 안전성 보장은 기본적인 보장, 강력한 보장, 예외 금지 보장이 있습니다.
2. 강력한 예외 안전성 보장은 '복사-후-맞바꾸기' 방법을 써서 구현할 수 있지만, 모든 함수에 대해 강력한 보장이 실용적인 것은 아닙니다.
3. 어떤 함수가 제공하는 예외 안전성 보장의 강도는, 그 함수가 내부적으로 호출하는 함수들이 제공하는 가장 약한 보장을 넘지 않습니다.
'언어 > C++' 카테고리의 다른 글
[Effective C++] 파일 사이의 컴파일 의존성을 최대로 줄이자 (0) | 2020.01.31 |
---|---|
[Effective C++] 인라인 함수는 미주알고주알 따져서 이해해 두자 (0) | 2020.01.30 |
[Effective C++] 내부에서 사용하는 객체에 대한 '핸들'을 반환하는 코드는 되도록 피하자 (0) | 2020.01.28 |
[Effective C++] 캐스팅은 절약, 또 절약! 잊지 말자 (0) | 2020.01.27 |
[Effective C++] 변수 정의는 늦출 수 있는 데까지 늦추는 근성을 발휘하자 (0) | 2020.01.26 |