2010-07-05 3 views
40

나는 소멸자가 스택의 정상적인 풀림과 예외가 발생할 때 호출되지만, exit()가 호출 될 때는 그렇지 않다는 것을 알고있다.어떤 상황에서 C++ 소멸자가 호출되지 않을까요?

소멸자가 불려 가지 않을 경우가 있습니까? SIGINT 나 SIGSEGV 같은 시그널은 어떨까요? 나는 SIGSEGV의 경우 호출되지 않는다고 추측하지만 SIGNINT의 경우 어떤 신호가 스택을 풀어 낼지 어떻게 알 수 있습니까?

전화를 걸지 않는 다른 상황이 있습니까?

+12

, 당신은 또한 알고 있어야 소멸자 때 호출되지 않습니다 전원 플러그가 당겨집니다;). –

+4

기본 동작을 무시하는 신호 처리기를 설치하지 않으면 SIGINT가 스택을 unwind하지 않습니다. 기본적으로 SIGINT는 프로그램을 즉시 종료시킵니다. – karunski

+0

질문에 대한 감독과 같아서 대답으로 게시하지 마십시오. 소멸자는 정적, 자동 또는 스레드 저장 기간을 가진 객체의 수명이 끝날 때 자동으로 (정상적인 상황에서) 호출됩니다. ** 동적 ** 저장 기간을 가진 객체의 경우 소멸자는 객체에 대한 포인터에서'delete'가 호출 될 때만 호출됩니다. 따라서 소멸자는'delete'가 호출되지 않는 동적 객체 (메모리 누수가 이것을 불가능하게 만들거나 감시에 의해 만들지 않아도)를 위해 호출되지 않을 것입니다. –

답변

45

[소멸자]가 호출되지 않는 다른 상황이 있습니까?

  1. 긴 점프 :이 프로세스를 풀기 자연 스택에 방해가 종종 C에서 정의되지 않은 동작이 발생할 ++.
  2. 미숙 한 출구 (예외가 이미 스택 된 상태에서 throw되는 예외가 정의되지 않은 동작으로 이어지는 동안 throw하는 것을주의 할 필요가 있지만 이미 예외를 지적 했음)
  3. Throwing 생성자에서 클래스에 대한 dtor을 호출하지 않습니다. 따라서 ctor에서 여러 포인터 (스마트 포인터가 아닌)로 관리되는 여러 메모리 블록을 할당하는 경우 함수 수준 try 블록을 사용하거나 이니셜 라이저 목록을 사용하지 않고 ctor에서 try/catch 블록을 사용해야합니다 (또는 dtor 클래스가 호출되지 않아도 initializer 목록에서 성공적으로 초기화 된 멤버가 파괴되므로 scoped_ptr과 같은 스마트 포인터를 사용하는 것이 좋습니다.
  4. 지적한대로 클래스가 기본 포인터를 통해 삭제 될 때 dtor 가상을 만들지 못하면 서브 클래스 dtors (정의되지 않은 동작)를 호출하지 못할 수 있습니다.
  5. 운영자 new/new [] 호출에 대해 일치하는 연산자 delete/delete []를 호출하지 못했습니다. 정의되지 않은 동작 - dtor 호출에 실패 할 수 있습니다.
  6. deallocate 섹션에서 사용자 정의 메모리 할당자를 사용하여 새 배치를 사용할 때 수동으로 dtor를 호출하지 못했습니다.
  7. memcpy와 같은 기능을 사용하면 복사 블록을 호출하지 않고 하나의 메모리 블록 만 다른 메모리 블록으로 복사합니다. mem * 함수는 클래스의 개인 데이터를 불도저로 쓰거나 vtable을 덮어 쓰는 등 치명적입니다. 결과는 일반적으로 정의되지 않은 동작입니다. 불완전한 유형에 스마트 포인터 (auto_ptr은) 일부의
  8. 인스턴스화이 discussion
+4

좋은 목록이지만, 포인트 3에 결함이 있습니다 : 실제로 이니셜 라이저 목록 주위에 try 블록을 넣고 함수 레벨 try 블록을 찾으십시오 :'struct X {X() try : x_ (42) {} catch (. ..) {} private : int x_; }}'void foo() try {} catch (...) {}'와 같은 함수에 실제로 이것을 사용할 수 있지만 몇 가지 중요한 컴파일러 (VS2008은 이후 버전에서 수정 될지 모르겠습니다) 그것. 스마트 포인터에 대한 조언은 유효합니다. –

+0

@ Fabio 감사합니다 Fabio, 나는 그것을 지적 할 것입니다! 나는 이러한 기능 수준의 try/catch 블록을 인식하지 못했습니다 (두통을 완화하기 위해 RAII를 모든 곳에서 사용하는 것을 선호합니다). – stinky472

+0

일반적으로 멋진 목록입니다. 그러나 # 4와 # 5는 기술적으로 정의되지 않은 동작이므로 표준에서 소멸자가 호출되는지 여부는 말할 것도 없습니다. 대부분의 컴파일러는 여러분이 말하는 것처럼 행동 할 것입니다 (또는 충돌 할 수도 있습니다). – KeithB

2

abort은 표준이 말한 것처럼 자동 또는 정적 저장 기간의 객체에 대한 소멸자를 실행하지 않고 프로그램을 종료합니다. 다른 상황에서는 구현 관련 문서를 읽어야합니다. 그것은 당신의 객체가 존재하지 않는 자신의 스택과 다른 실행 컨텍스트이기 때문에

3

자체에 의한 신호 현재의 thread 따라서 소멸자의 호출의 실행에 영향을 미치지 않습니다. 마치 인터럽트와 같습니다. 실행 컨텍스트 외부에서 처리되며, 처리되면 컨트롤이 프로그램에 반환됩니다.

멀티 스레딩과 마찬가지로 C++ 언어은 신호 개념을 알지 못합니다. 이 두 개는 서로 완전히 직교하며 서로 관련이없는 두 개의 표준에 의해 지정됩니다. 그들이 상호 작용하는 방법은 표준 중 하나를 위반하지 않는 한 구현에 달려 있습니다.

또 다른 경우는 객체의 소멸자가 호출되지 않는 경우는 생성자가 예외를 throw하는 경우입니다. 그러나 소멸자는 여전히 소집됩니다.

7

C++ 표준은 처리해야하는 특정 신호에 대해 아무것도 말하지 않는다 - exit() 또는 abort() 또는 terminate()를 호출하는 경우 많은 구현이 SIGINT을 지원하지 않을 수 있습니다를, 등 소멸자가 호출되지 않습니다.

편집

:은 그냥 C++ 표준을 통해 빠른 검색을 했어 나는 신호가 객체 수명과 상호 작용하는 방법을 지정 아무것도 찾을 수 없습니다 - 아마 나보다 더 나은 표준 쿵푸을 가진 사람이 뭔가를 찾을 수 있습니까?

또한 편집 : 다른 질문에 대답하는 동안, 나는 표준이 발견 : 모든 구성 객체 을 촉구 출구에

범위에서 (그러나 달성), 소멸자 (12.4)입니다 해당 범위에서 선언 된 (3.7.2) (명명 된 오브젝트 또는 임시 저장 영역) 의 선언의 역순으로

그래서 신호 수신시 소멸자를 호출해야합니다.

+0

신호를 받으면 프로그램 컨트롤이 범위를 벗어나지 않습니다. 그래서 인용 된 표준은 적용되지 않습니다. POSIX 시그널 핸들러의 기본 동작은 스택을 풀거나 파괴하지 않습니다. – karunski

+1

@karunski 시그널 핸들러가 설치되어 있다면 확실히 스코프를 빠져 나간다. –

+0

@Neil Butterworth 물론, 기본적으로 그런 것은 아닙니다. 보다 정확한 결론은 "신호가 처리 된 후 (소집되지 않고) 신호 처리기가 호출 된 지점으로 소멸자를 호출해야합니다"라고 말합니다. – karunski

1

소멸자가 호출되는 기본적으로 두 가지 상황이 있습니다. 함수 (또는 예외)의 끝에서 스택 unwind에서 누군가 (또는 참조 카운터)가 delete를 호출하면.

한 가지 특수한 상황은 정적 객체에서 발견 될 수 있습니다.이 객체는 at_exit을 통해 프로그램 끝에서 소멸되지만 여전히 두 번째 상황입니다.

어떤 신호가 at_exit를 통과할지 결정할 수 있습니다. kill -9는 프로세스를 즉시 중지시키고 다른 신호는 신호를 종료하지만 신호 콜백에 얼마나 의존 하는지를 알려줍니다.

3

다형성을 사용하고 기본 소멸자를 가상으로 만들지 않은 경우 호출되지 않는 또 다른 경우입니다.

+1

이 경우 정의되지 않은 동작이 발생합니다. –

2

함수 있는지 또는 방법은이 사양을 던져 가지고 있으며, 사양에 포함되지 뭔가, 기본 동작이다 던졌습니다 즉시 종료하십시오. 스택이 풀리지 않고 소멸자가 호출되지 않습니다.

POSIX 신호는 운영 체제에 특정한 구문이며 C++ 개체 범위에 대한 개념이 없습니다. 일반적으로 어쩌면 신호를 제외하고는 아무 것도 할 수 없으며, 전역 플래그 변수를 설정 한 다음 나중에 시그널 핸들러가 종료 된 후 C++ 코드에서 처리합니다.

최근 버전의 GCC에서는 동기식 신호 처리기 내에서 예외를 throw 할 수 있으므로 예상되는 풀기 및 제거 프로세스가 발생합니다. 이것은 매우 운영 체제와 컴파일러에 따라 다르지만,

2

많은 답변이 있지만 여전히 불완전합니다!

소멸자가 실행되지 않은 또 다른 사례를 발견했습니다. 예외가 라이브러리 경계에서 catch되면 항상 발생합니다.

여기에서 자세한 내용을 참조하십시오 http://thedailywtf.com/Articles/My-Tales.aspx, 여기에 지적

Destructors not executed (no stack unwinding) when exception is thrown

+0

이것은 C++ 사양이 그러한 동작을 허용하는지 의심스러워하는 버그처럼 보입니다. – WilliamKF

+0

여기에있는 질문은 "어떤 상황에서 소멸자가 불리지 않는다"입니다. Visual Studio의 버그 일지라도 버그가 원인이기도하므로이 질문에 대한 유효한 답변입니다. 사람들은 Google에서 여기에 와서 일부는 버그인지 여부를 모른 채이 특정 문제 만 경험할 수 있습니다. – Elmue

관련 문제