프로그래밍C++ 백엔드 개발자

C++에서 예외 처리를 위한 스택 언와인딩과 자원의 차이점을 설명하세요. 자원을 올바르게 해제하는 것을 보장하는 방법은 무엇입니까?

Hintsage AI 어시스턴트로 면접 통과

답변.

문제의 역사:

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은 닫힐 것이다

주요 특징:

  • 스택 언와인딩은 예외 발생 시 객체를 파괴하는 표준 메커니즘입니다.
  • 자원 해제는 항상 소멸자에서 수행해야 합니다.
  • RAII 또는 표준 클래스를 사용하십시오(예: 스마트 포인터).

함정 질문.

코드에 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)에 대해서는 보장하지 않습니다.

일반적인 오류 및 안티패턴

  • try-catch 내에서 자원을 수동으로 청소하며 소멸자를 무시함
  • RAII 또는 표준 클래스를 대신하여 원시 포인터를 사용함
  • 예외 처리를 추상화하여 자원이 해제되지 않도록 함(예: setjmp/longjmp)

실제 사례

부정적 사례

프로그래머가 new를 사용하여 메모리를 할당하고 예외를 처리하며 catch 블록에서 메모리를 해제하지만 함수의 다른 종료 경로를 잊어버립니다.

장점:

  • 처음에는 단순하고 "투명하게" 보임

단점:

  • 예외가 예상치 못한 위치에서 발생하면 메모리 리크가 발생할 수 있음
  • 테스트 및 유지 관리가 어렵고 복잡함

긍정적 사례

모든 자원에 대해 std::unique_ptr 및 RAII 클래스를 사용하여 해제는 try/catch에 의존하지 않습니다.

장점:

  • 자원 리크가 없음
  • 오류 처리 로직이 간단해짐

단점:

  • 표준 라이브러리 및 언어의 관용구에 대한 더 많은 이해가 필요함