몇 가지 방법이 있지만 먼저 객체 정리가 중요한 이유를 이해해야하므로 std::exit
C ++ 프로그래머들 사이에서 그 이유 가 소외됩니다.
RAII 및 스택 풀기
C ++은 RAII 라는 관용구를 사용하는데 , 이는 간단히 말해서 객체가 생성자에서 초기화를 수행하고 소멸자에서 정리해야 함을 의미합니다. 예를 들어 std::ofstream
클래스는 생성자 중에 파일을 열 수 있고, 사용자는 그 파일에 대해 출력 작업을 수행하고 마지막으로 수명주기의 끝에서 일반적으로 해당 범위에 따라 결정되며 소멸자는 본질적으로 파일을 닫고 플러시합니다. 디스크에 기록 된 내용
소멸자가 파일을 플러시하고 닫지 않으면 어떻게됩니까? 누가 알아! 그러나 파일에 쓰려고했던 모든 데이터를 쓰지 못할 수도 있습니다.
예를 들어이 코드를 고려하십시오
#include <fstream>
#include <exception>
#include <memory>
void inner_mad()
{
throw std::exception();
}
void mad()
{
auto ptr = std::make_unique<int>();
inner_mad();
}
int main()
{
std::ofstream os("file.txt");
os << "Content!!!";
int possibility = /* either 1, 2, 3 or 4 */;
if(possibility == 1)
return 0;
else if(possibility == 2)
throw std::exception();
else if(possibility == 3)
mad();
else if(possibility == 4)
exit(0);
}
각 가능성에서 일어나는 일은 :
- 가능성 1 : 리턴은 본질적으로 현재 기능 범위를
os
벗어나므로 소멸자를 호출하고 파일을 디스크로 플러시하여 적절한 정리를 수행 하는 수명주기의 끝에 대해 알고 있습니다.
- 가능성 2 : 예외를 던지면 현재 범위에있는 개체의 수명 주기도 관리하므로 적절한 정리가 수행됩니다.
- 가능성 3 : 스택 풀기 작업이 시작됩니다! 예외가 던져하더라도
inner_mad
, 언 와인 더가의 스택 불구하고 갈 것입니다 mad
및 main
적절한 정리를 수행하는 모든 객체를 포함하여, 적절하게 파괴 될거야 ptr
하고 os
.
- 가능성 4 : 여기 요?
exit
C 함수이며 C ++ 숙어를 인식하거나 호환하지 않습니다. 그것은 하지 않습니다 포함하여 개체에 대한 정리를 수행 os
매우 동일한 범위에. 따라서 파일이 제대로 닫히지 않기 때문에 내용이 파일에 쓰여지지 않을 수 있습니다!
- 기타 가능성 : 암시 적 수행을 통해
return 0
가능성 1과 동일한 효과, 즉 적절한 정리와 같은 방식으로 주요 범위를 벗어나게 됩니다.
그러나 내가 방금 말한 것에 대해 확신하지 마십시오 (주로 가능성 2와 3). 계속 읽고 우리는 적절한 예외 기반 정리를 수행하는 방법을 알아낼 것입니다.
가능한 방법으로 종료
메인에서 돌아온다!
가능할 때마다이 작업을 수행해야합니다. 항상 메인에서 적절한 종료 상태를 반환하여 프로그램에서 돌아 오는 것을 선호합니다.
프로그램 호출자와 운영 체제는 프로그램이 수행해야 할 작업이 성공적으로 수행되었는지 여부를 알고 싶어 할 수 있습니다. 이와 같은 이유로 0을 반환하거나 EXIT_SUCCESS
프로그램이 성공적으로 종료 되었음을 알리고 프로그램이 성공적으로 종료되었다는 신호를 EXIT_FAILURE
보내려면 다른 형태의 반환 값이 구현 정의되어 있습니다 ( §18.5 / 8 ).
그러나 당신은 호출 스택에 매우 깊이있을 수 있으며, 그것을 모두 반환하는 것은 고통 스러울 수 있습니다 ...
[예외] 예외를 던져
예외를 던지면 이전 범위에서 모든 객체의 소멸자를 호출하여 스택 해제를 사용하여 적절한 객체 정리를 수행합니다.
그러나 여기 에 캐치가 있습니다 ! throw 된 예외가 처리되지 않을 때 (catch (...) 절noexcept
에 의해) 호출 스택의 중간에 함수 가있는 경우에도 스택 해제가 수행되는지 여부가 구현에서 정의됩니다 . 이것은 §15.5.1에 명시되어 있다 .
미묘한 오류 처리 기술을 줄이려면 예외 처리를 포기해야하는 경우도 있습니다. [참고 : 이러한 상황은 다음과 같습니다.
[...]
— 예외 처리 메커니즘이 발생 된 예외 (15.3)에 대한 처리기를 찾을 수 없거나 처리기 검색 (15.3)noexcept
에서 예외를 허용하지 않는 -사양 을 가진 함수의 가장 바깥 쪽 블록이 발견 될 때 (15.4) [...]
[...]
이러한 경우 std :: terminate ()가 (18.8.3)이라고합니다. 일치하는 핸들러가없는 경우 std :: terminate ()가 호출되기 전에 스택이 풀리는 지 여부는 구현에서 정의됩니다. [...]
그래서 우리는 그것을 잡아야합니다!
예외를 던져서 메인에서 잡아라!
잡히지 않은 예외는 스택 해제를 수행하지 않을 수 있고 (따라서 적절한 정리를 수행 하지 않을 수 있기 때문에 ) 메인에서 예외를 포착 한 다음 종료 상태 ( EXIT_SUCCESS
또는 EXIT_FAILURE
) 를 반환해야합니다 .
따라서 좋은 설정은 다음과 같습니다.
int main()
{
/* ... */
try
{
// Insert code that will return by throwing a exception.
}
catch(const std::exception&) // Consider using a custom exception type for intentional
{ // throws. A good idea might be a `return_exception`.
return EXIT_FAILURE;
}
/* ... */
}
[하지 마십시오] std :: exit
이것은 어떤 종류의 스택 해제도 수행하지 않으며 스택의 살아있는 객체는 해당 소멸자를 호출하여 정리를 수행하지 않습니다.
이것은 §3.6.1 / 4 [basic.start.init] 에서 시행됩니다 .
현재 블록을 떠나지 않고 프로그램을 종료하면 (예 : std :: exit (int) (18.5) 함수를 호출하여) 자동 저장 기간 (12.4)이있는 객체는 파괴되지 않습니다 . 정적 또는 스레드 저장 기간을 가진 오브젝트를 소멸하는 동안 프로그램을 종료하기 위해 std :: exit를 호출하면 프로그램에 정의되지 않은 동작이 있습니다.
지금 생각해보십시오. 왜 그런 일을 하시겠습니까? 고통스럽게 몇 개의 물건을 손상 시켰습니까?
다른 [나쁜] 대안
프로그램을 종료하는 다른 방법 (충돌 이외) 이 있지만 권장되지는 않습니다. 설명을 위해 여기에 제시 할 것입니다. 공지 사항 어떻게 정상적인 프로그램 종료가 되지 않습니다 평균 스택 풀기하지만, 괜찮 운영 체제 상태.
main()
사용 회수, 기능에 적절한 리턴 값을 사용하거나 적절한 예외를 발생. 하지 마십시오 사용exit()
!