Namespaces
Variants

Destructors

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

소멸자는 객체의 멤버 함수 로서 객체의 수명이 종료 될 때 호출되는 특별한 함수입니다. 소멸자의 목적은 객체가 자신의 수명 동안 획득했을 수 있는 자원을 해제하는 것입니다.

소멸자는 코루틴 이 될 수 없습니다.

(C++20부터)

목차

구문

소멸자 (C++20까지) 예정 소멸자 (C++20부터) 는 다음 형식의 멤버 함수 선언자 를 사용하여 선언됩니다:

class-name-with-tilde ( parameter-list  (선택 사항) ) except  (선택 사항) attr  (선택 사항)
class-name-with-tilde - 식별자 표현식, 뒤이어 속성 목록이 올 수 있으며, (C++11부터) 선택적으로 괄호 쌍으로 둘러싸일 수 있음
parameter-list - 매개변수 목록 (반드시 비어있거나 void 여야 함)
except -

동적 예외 명세

(C++11까지)

동적 예외 명세 또는 noexcept 명세

(C++11부터)
(C++17까지)

noexcept 명세

(C++17부터)
attr - (C++11부터) 속성 목록 attributes

소멸자 선언의 선언 지정자 에서 허용되는 유일한 지정자는 예비 (C++20부터) constexpr , (C++11부터) friend , inline 그리고 virtual 입니다 (특히, 반환 타입은 허용되지 않습니다).

class-name-with-tilde 식별자 표현식은 다음 형식 중 하나를 가져야 합니다:

  • 클래스의 경우, 식별자 표현식은 ~ 뒤에 바로 바깥쪽 클래스의 injected-class-name 이 옵니다.
  • 클래스 템플릿의 경우, 식별자 표현식은 ~ 뒤에 current instantiation 를 나타내는 클래스 이름 (C++20까지) injected-class-name (C++20부터) 이 옵니다.
  • 그렇지 않으면, 식별자 표현식은 터미널 비한정 식별자가 ~ 다음에 한정 식별자의 비터미널 부분이 지정하는 클래스의 주입된 클래스 이름이 오는 한정 식별자입니다.

설명

소멸자는 객체의 lifetime 이 종료될 때마다 암시적으로 호출되며, 여기에는 다음이 포함됩니다

  • 스레드 로컬 저장 기간을 가진 객체들에 대한 스레드 종료
(since C++11)
  • 자동 저장 기간을 가진 객체와 참조에 바인딩되어 수명이 연장된 임시 객체의 경우, 범위 종료 시
  • delete expression , 동적 저장 기간을 가진 객체의 경우
  • 이름 없는 임시 객체의 경우, 전체 expression 종료 시
  • stack unwinding , 예외가 해당 블록을 벗어날 때 자동 저장 기간을 가진 객체의 경우 (처리되지 않은 예외)

소멸자는 명시적으로 호출할 수도 있습니다.

잠재적 소멸자 (Prospective destructor)

클래스는 하나 이상의 잠재적 소멸자를 가질 수 있으며, 그 중 하나가 클래스의 소멸자로 선택됩니다.

어떤 잠재적 소멸자가 소멸자인지 결정하기 위해, 클래스 정의의 끝에서 오버로드 해결 이 빈 인수 목록으로 클래스 내에서 선언된 잠재적 소멸자들 사이에서 수행됩니다. 오버로드 해결이 실패하면 프로그램은 잘못된 형식입니다. 소멸자 선택은 선택된 소멸자를 ODR-사용 하지 않으며, 선택된 소멸자는 삭제될 수 있습니다.

모든 잠재적 소멸자는 특수 멤버 함수입니다. 클래스 T 에 대해 사용자 선언 잠재적 소멸자가 제공되지 않으면, 컴파일러는 항상 암시적으로 선언 하며, 암시적으로 선언된 잠재적 소멸자는 T 의 소멸자이기도 합니다.

#include <cstdio>
#include <type_traits>
template<typename T>
struct A
{
    ~A() requires std::is_integral_v<T> { std::puts("~A, T is integral"); }
    ~A() requires std::is_pointer_v<T> { std::puts("~A, T is a pointer"); }
    ~A() { std::puts("~A, T is anything else"); }
};
int main()
{
    A<int> a;
    A<int*> b;
    A<float> c;
}

출력:

~A, T is anything else
~A, T is a pointer
~A, T is integral
(C++20부터)

잠재적으로 호출되는 소멸자

클래스 T 의 소멸자가 잠재적으로 호출될 수 있는 상황은 다음과 같습니다:

잠재적으로 호출될 수 있는 소멸자가 삭제되었거나 (C++11부터) 호출 문맥에서 접근할 수 없는 경우, 프로그램은 ill-formed입니다.

암시적으로 선언된 소멸자

사용자가 선언한 prospective (since C++20) destructor가 제공되지 않는 경우, class type 에 대해 컴파일러는 항상 destructor를 inline public 멤버로 선언합니다.

다른 암시적으로 선언된 특별 멤버 함수와 마찬가지로, 암시적으로 선언된 소멸자의 예외 명세는 다음 경우를 제외하고 non-throwing입니다: 잠재적으로 생성된 기반 클래스나 멤버의 소멸자가 potentially-throwing 인 경우 (C++17부터) 암시적 정의가 다른 예외 명세를 가진 함수를 직접 호출하는 경우 (C++17 이전) . 실제로, 암시적 소멸자는 noexcept 입니다. 단, 소멸자가 noexcept ( false ) 인 기반 클래스나 멤버에 의해 "오염된" 클래스는 예외입니다.

암시적으로 정의된 소멸자

암시적으로 선언된 소멸자가 삭제되지 않은 경우, 컴파일러에 의해 ODR-사용 될 때 암시적으로 정의됩니다(즉, 함수 본문이 생성되고 컴파일됨). 이 암시적으로 정의된 소멸자는 빈 본문을 가집니다.

이것이 constexpr destructor (until C++23) constexpr function (since C++23) 의 요구사항을 만족하는 경우, 생성된 소멸자는 constexpr 입니다.

(since C++20)


삭제된 소멸자

다음 조건 중 하나라도 만족할 경우, 클래스 T 의 암시적으로 선언되거나 명시적으로 기본 설정된 소멸자는 삭제된 것으로 정의됩니다:

  • 삭제되었거나 T 의 소멸자에서 접근할 수 없거나,
  • 하위 객체가 variant 멤버 인 경우, 비트리비얼(non-trivial)인 경우.
(C++26 이전)
  • T 가 union이 아니고, 클래스 타입 M (또는 다차원 배열)의 non-variant 잠재적으로 생성된 하위 객체 를 가지고 있으며, M 이 삭제되었거나 T 의 소멸자에서 접근할 수 없는 소멸자를 가지는 경우.
  • T 가 union이고, 다음 조건 중 하나라도 만족하는 경우:
  • T 타입 객체를 기본 초기화하기 위한 생성자 선택을 위한 오버로드 해결이 실패하거나, 삭제되었거나 비트리비얼인 생성자를 선택하는 경우.
  • T 가 클래스 타입 M (또는 다차원 배열)의 variant 멤버 V 를 가지고 있고, V 가 기본 초기화자를 가지며 M 이 비트리비얼 소멸자를 가지는 경우.
(C++26 이후)
  • 소멸자가 가상(virtual)이고 할당 해제 함수 에 대한 조회 결과가 다음 중 하나인 경우:
  • 모호성(ambiguity)이 발생하거나,
  • 삭제되었거나 소멸자에서 접근할 수 없는 함수인 경우.

T 에 대한 명시적으로 기본 설정된 예상 소멸자(prospective destructor)가 T 의 소멸자가 아닌 경우 삭제된 것으로 정의됩니다.

(C++20 이후)
(C++11 이후)

Trivial destructor

클래스 T 의 소멸자는 다음 조건들이 모두 충족될 때 trivial합니다:

  • 소멸자는 암시적으로 선언됨 (C++11까지) 사용자 제공이 아님 user-provided (C++11부터) .
  • 소멸자는 가상이 아닙니다.
  • 모든 직접 기본 클래스는 trivial 소멸자를 가집니다.
  • 클래스 타입(또는 클래스 타입의 배열)인 모든 비정적 데이터 멤버가 trivial destructor를 가짐
(C++26 이전)
  • T 가 union이거나, 클래스 타입(또는 클래스 타입의 배열)인 모든 비variant 비정적 데이터 멤버가 trivial destructor를 가짐
(C++26 이후)

trivial destructor(사소한 소멸자)는 아무런 동작도 수행하지 않는 소멸자입니다. trivial destructor를 가진 객체들은 delete 표현식이 필요하지 않으며 단순히 해당 저장 공간을 할당 해제함으로써 처리될 수 있습니다. C 언어와 호환되는 모든 데이터 타입(POD 타입)들은 사소하게 소멸 가능합니다.

소멸 순서

사용자 정의 또는 암시적으로 정의된 소멸자 모두에 대해, 소멸자 본문을 실행하고 본문 내에서 할당된 자동 객체들을 파괴한 후, 컴파일러는 클래스의 모든 비정적(non-static) 비변형(non-variant) 데이터 멤버들의 소멸자들을 선언 순서의 역순으로 호출합니다. 그 다음 모든 직접 비가상(direct non-virtual) 기본 클래스들의 소멸자들을 생성 순서의 역순 으로 호출합니다(이들은 차례로 자신의 멤버들과 기본 클래스들의 소멸자들을 호출합니다). 그리고 나서, 이 객체가 최종 파생 클래스(most derived class) 인 경우, 모든 가상 기본 클래스(virtual bases)들의 소멸자들을 호출합니다.

소멸자가 직접 호출되는 경우에도 (예: obj.~Foo ( ) ; ), return 문이 ~Foo ( ) 에 있더라도 호출자에게 제어를 즉시 반환하지 않습니다: 먼저 모든 멤버 및 기본 클래스 소멸자들을 호출합니다.

가상 소멸자

베이스 클래스에 대한 포인터를 통해 객체를 삭제할 때, 베이스 클래스의 소멸자가 virtual 이 아닌 경우 정의되지 않은 동작(undefined behavior)을 유발합니다:

class Base
{
public:
    virtual ~Base() {}
};
class Derived : public Base {};
Base* b = new Derived;
delete b; // 안전함

일반적인 지침은 기본 클래스의 소멸자가 public이면서 virtual이거나 protected이면서 nonvirtual이어야 한다 는 것입니다.

순수 가상 소멸자

A prospective (since C++20) destructor는 pure virtual 로 선언될 수 있습니다. 예를 들어, 추상화되어야 하지만 pure virtual로 선언될 수 있는 다른 적절한 함수가 없는 기본 클래스에서 사용됩니다. pure virtual destructor는 정의를 가져야 합니다. 파생 클래스가 소멸될 때 모든 기본 클래스 destructor들이 항상 호출되기 때문입니다:

class AbstractBase
{
public:
    virtual ~AbstractBase() = 0;
};
AbstractBase::~AbstractBase() {}
class Derived : public AbstractBase {};
// AbstractBase obj; // 컴파일러 오류
Derived obj;         // 정상

예외

다른 함수와 마찬가지로, 소멸자도 예외를 발생시키며 종료할 수 있습니다 exception (이는 일반적으로 명시적으로 noexcept ( false ) 로 선언해야 함) (since C++11) , 그러나 이 소멸자가 stack unwinding 중에 호출되는 경우에는 std::terminate 가 대신 호출됩니다.

비록 std::uncaught_exceptions 가 때때로 진행 중인 스택 풀기(stack unwinding)를 감지하는 데 사용될 수 있지만, 소멸자가 예외를 던져서 종료되도록 허용하는 것은 일반적으로 나쁜 관행으로 간주됩니다. 그럼에도 불구하고 이 기능은 SOCI Galera 3 와 같은 일부 라이브러리에서 사용되며, 이러한 라이브러리들은 이름 없는 임시 객체의 소멸자들이 해당 임시 객체를 생성하는 전체 표현식의 끝에서 예외를 던질 수 있는 능력에 의존합니다.

std::experimental::scope_success 는 Library fundamental TS v3에서 예외를 던질 수 있는 소멸자 를 가질 수 있으며, 이는 스코프가 정상적으로 종료되고 exit 함수가 예외를 던질 때 예외를 발생시킵니다.

참고 사항

일반 객체(예: 지역 변수)에 대해 소멸자를 직접 호출하면, 범위의 끝에서 소멸자가 다시 호출될 때 정의되지 않은 동작을 초래합니다.

일반적인 맥락에서, 소멸자 호출 구문은 비클래스 타입의 객체와 함께 사용될 수 있습니다; 이를 의사 소멸자 호출이라고 합니다: 멤버 접근 연산자 를 참조하십시오.

기능 테스트 매크로 표준 기능
__cpp_trivial_union 202502L (C++26) union의 특수 멤버 함수에 대한 triviality 요구사항 완화

예제

#include <iostream>
struct A
{
    int i;
    A(int num) : i(num)
    {
        std::cout << "ctor a" << i << '\n';
    }
    (~A)() // but usually ~A()
    {
        std::cout << "dtor a" << i << '\n';
    }
};
A a0(0);
int main()
{
    A a1(1);
    A* p;
    { // nested scope
        A a2(2);
        p = new A(3);
    } // a2 out of scope
    delete p; // calls the destructor of a3
}

출력:

ctor a0
ctor a1
ctor a2
ctor a3
dtor a2
dtor a3
dtor a1
dtor a0

결함 보고서

다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
CWG 193 C++98 소멸자 내 자동 객체들이 클래스의 기반 및 멤버 하위 객체들
파괴 전후 중 언제 파괴되는지 명시되지 않음
해당 하위 객체들을
파괴하기 전에
파괴됨
CWG 344 C++98 소멸자의 선언자 구문이 결함이 있음 ( CWG 이슈 194 CWG 이슈 263 과 동일한 문제점) 전문화된 함수 선언자
구문으로 변경
CWG 1241 C++98 정적 멤버들이 소멸자 실행 직후
파괴될 수 있음
비정적 멤버들만
파괴
CWG 1353 C++98 암시적으로 선언된 소멸자들이 정의되지 않는 조건에서
다차원 배열 타입을 고려하지 않음
해당 타입들을 고려함
CWG 1435 C++98 소멸자 선언자 구문에서 "클래스 이름"의
의미가 불명확함
전문화된 함수 선언자
구문으로 변경
CWG 2180 C++98 최종 파생 클래스가 아닌 클래스의 소멸자가
가상 직접 기반 클래스들의 소멸자들을 호출함
해당 소멸자들을 호출하지 않음
CWG 2807 C++20 선언 지정자가 consteval 을 포함할 수 있음 금지됨

참고 항목