예외 명세

·2024년 12월 18일
0

C++

목록 보기
25/26

예외를 방출하지 않을 함수는 noexcept로 선언하라

noexcept

  • 해당 함수가 예외를 방출하지 않을 것임을 명시하는 키워드
  • 해당 키워드를 사용하면 컴파일러가 더 나은 목적 코드를 산출할 수 있음
int f(int x) throw(); 	 // C++98 방식
int f(int x) noexcept(); // C++11 방식

C++98에서는 예외 명세가 위반되면 호출 스택이 f를 호출한 지점에 도달할 때까지 풀리며 그 지점에서 몇 가지 동작이 취해진 후 프로그램 실행이 종료됨
C++11에서는 프로그램 실행이 종료되기 전에 호출 스택이 풀릴 수도 있고 풀리지 않을 수도 있음

호출 스택이 풀리는 것과 풀릴 수도 있는 것의 차이는 컴파일러의 코드 작성에 영향을 미침

noexcept 함수에서 컴파일러의 옵티마이저는 예외가 함수 바깥으로 전파될 수 있다고 해도 실행지점 스택을 풀기 가능 상태로 유지할 필요가 없음
또한 예외가 noexcept 함수를 벗어난다고 해도 noexcept 함수 안의 객체들을 반드시 생성의 반대 순서로 파괴해야 하는 것도 아님
그러나 예외 명세가 throw()인 함수에는 그러한 최적화 유연성이 없으며, 예외 명세가 아예 없는 함수 역시 마찬가지로 그런 유연성이 없음

예외를 방출하지 않으면 다 noexcept 키워드를 붙이면 되는거 아닌가?

대부분의 함수는 예외에 중립적임
예외 중립적 함수는 스스로 예외를 던지지는 않지만 예외를 던지는 다른 함수를 호출할 수 있음
다른 함수가 예외를 던지면 예외 중립적 함수는 그 예외를 그대로 통과시킴
이처럼 그냥 통과하는 예외가 존재할 수 있으므로 예외 중립적 함수는 noexcept 가 될 수 없음

만약 noexcept 로 선언하고 나중에 마음을 바꾸면 클라이언트 코드가 깨질 위험이 생김
noexcept 는 함수의 인터페이스의 일부로 함수의 구현이 예외를 방출하지 않는다는 성질을 오랫동안 유지할 결심이 선 경우에만 함수를 noexcept 로 선언해야 함

기본적으로 모든 메모리 해제 함수와 소멸자는 암묵적으로 noexcept 이기 때문에 직접 선언할 필요 없음
(직접 선언해도 해가 되지는 않지만 일반적으로 키워드를 쓰지 않음)

정리

  • noexcept 는 함수의 인터페이스의 일부이며 호출자가 noexcept 여부에 의존할 수 있음
  • noexcept 함수는 비noexcept 함수보다 최적화 여지가 큼
  • noexcept 는 이동 연산자들과 swap, 메모리 해제 함수들 그리고 소멸자에 특히나 유용함
  • 대부분의 함수는 예외에 중립적임

추가 학습

스택 언와인딩(Stack Unwinding)

  • 함수 호출 스택을 풀어서 해당 함수가 소유한 자원을 해제하고 소멸자를 호출하는 작업
  • noexcept 함수에서는 이런 스택 언와인딩을 준비할 필요가 없기 때문에 컴파일러는 호출 스택을 간단히 처리할 수 있음
    -> 결과적으로 오버헤드 감소 및 더 간결한 목적 코드를 생성할 수 있음

이 외에도 컴파일러에게 예외 처리와 관련된 메타데이터 및 런타임 동작을 제거할 수 있는 힌트를 줌

  • 함수 호출과 관련된 런타임 예외 검사 제거
  • 예외 처리 테이블 생성 생략
  • 더 단순한 레지스터 관리 및 코드를 인라인 처리 가능

목적 코드(Object Code)

컴파일러에서 번역 단위별로 컴파일을 수행한 결과 생성된 파일이 목적 파일인데 그 안에 포함된 것이 목적 코드임
CPU가 실행할 수 있는 형태에 가깝지만 완전한 실행파일은 아님

목적 파일(Object File) 내부에는 어떤 정보가 들어갈까?

  • 목적 코드
  • 심볼 테이블 : 함수와 변수의 정의/참조 정보
  • 재배치 정보 : 코드/데이터의 주소 재배치 정보
  • 디버깅 정보 : 디버깅을 위한 메타데이터
  • 섹션 헤더 : 각 섹션(코드, 데이터 등)의 위치와 크기 정보
  • 라이브러리 참조 정보 : 필요한 외부 라이브러리 정보

0개의 댓글

관련 채용 정보