문제의 역사:
C++은 성능에 중점을 두고 설계되었기 때문에 자원(메모리, 파일, 스트림, 소켓)을 수동으로 관리하는 경우가 많습니다. 예외가 발생하면 캡처된 자원을 정리해야 합니다. 스택 언와인딩(stack unwinding)은 예외가 발생할 때 C++이 함수의 작업을 올바르게 종료하기 위해 사용하는 메커니즘입니다.
문제:
예외가 발생하면 제어는 즉시 catch 블록으로 이동하고 중간 함수는 "언와인딩"됩니다: 그들의 소멸자가 호출되지만 명시적인 해제 함수 호출은 건너뛰어질 수 있습니다(예: 자원을 자동으로 해제하는 객체가 사용되지 않는 경우).
해결책:
C++에서 자원 해제는 해제 함수를 수동으로 호출하기보다 소멸자에 맡기는 것이 좋습니다. RAII(자원 확보는 초기화이다) 패턴은 자원 해제를 자동화하는 명확한 방법입니다. 스택 언와인딩 시 소멸자가 호출되어 자원이 함수의 종료 경로와 상관없이 해제됩니다.
코드 예:
#include <fstream> #include <stdexcept> void readFile(const std::string& filename) { std::ifstream file(filename); // 예외가 발생해도 자동으로 열리고 닫힌다 if (!file.is_open()) { throw std::runtime_error("파일을 열 수 없습니다"); } // ... 파일 읽기 } // 예외가 발생해도 file은 닫힐 것이다
주요 특징:
코드에 delete ptr;가 catch 블록에 있으면 메모리 해제가 충분합니까?
아니요, 메모리가 할당된 것과 catch 블록 사이에 예외가 발생하면 메모리가 해제되지 않을 수 있습니다. 더 좋은 방법은 std::unique_ptr를 사용하거나 소멸자에서 delete를 작성하는 것입니다.
코드 예:
void foo() { int* data = new int[10]; // ... throw std::runtime_error("실패"); delete[] data; // 예외 발생 시 호출되지 않음 }
스택 언와인딩이 스택에 있는 객체의 소멸자 호출을 건너뛸 수 있습니까?
아니요, 예외 발생 지점 이전에 파괴되지 않은 모든 지역 객체는 생성 순서의 역순으로 파괴되며, 소멸자는 보장되게 호출됩니다.
try 블록에서 나가기 위해 goto 또는 longjmp를 사용할 수 있고 소멸자 호출을 기대할 수 있습니까?
아니요. C++은 예외로 인한 스택 언와인딩 시에만 소멸자 호출을 보장하며, 잘못된 흐름 관리(goto, setjmp/longjmp)에 대해서는 보장하지 않습니다.
프로그래머가 new를 사용하여 메모리를 할당하고 예외를 처리하며 catch 블록에서 메모리를 해제하지만 함수의 다른 종료 경로를 잊어버립니다.
장점:
단점:
모든 자원에 대해 std::unique_ptr 및 RAII 클래스를 사용하여 해제는 try/catch에 의존하지 않습니다.
장점:
단점: