소멸자 (컴퓨터 프로그래밍)
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
소멸자는 객체의 수명이 다할 때 시스템 자원을 해제하기 위해 사용되는 특별한 메서드이다. C++에서는 클래스 이름 앞에 물결표(~)를 붙여 소멸자를 정의하며, 객체가 범위를 벗어나거나 `delete` 연산자가 적용될 때 호출된다. 다른 프로그래밍 언어에서도 소멸자와 유사한 기능을 제공하며, 오브젝트 파스칼에서는 `destructor`, 펄에서는 `DESTROY` 또는 `DEMOLISH`, 파이썬에서는 `__del__`, Objective-C에서는 `dealloc`, PHP 5에서는 `__destruct`, D에서는 `~this()`, Rust에서는 `drop`, Swift에서는 `deinit` 등의 명칭을 사용한다. 소멸자는 예외를 발생시키면 안 되며, C++에서는 가상 소멸자를 통해 상속 관계에서 파생 클래스의 소멸자가 올바르게 호출되도록 보장한다. 가비지 컬렉션을 사용하는 언어에서는 파이널라이저를 통해 소멸자와 유사한 기능을 수행하기도 한다.
더 읽어볼만한 페이지
- 메소드 (컴퓨터 프로그래밍) - 동적 디스패치
동적 디스패치는 프로그램 실행 시 호출할 메서드를 결정하는 메커니즘으로, 단일 또는 다중 객체 기준으로 선택하며, 유연성과 확장성을 높이지만 성능 오버헤드를 발생시킬 수 있다. - 메소드 (컴퓨터 프로그래밍) - 생성자
생성자는 객체 지향 프로그래밍에서 클래스의 인스턴스를 생성하고 초기화하며, 클래스 기반 언어에서는 클래스명과 동일한 이름을 가지고, JavaScript에서는 `new` 연산자와 함께 함수를 호출하거나 `class` 키워드를 통해 정의한다. - C++ - 헤더 파일
헤더 파일은 프로그래밍 언어에서 코드 재사용성, 모듈화, 컴파일 시간 단축에 기여하며 함수 프로토타입, 변수 선언 등을 포함하고 `#include` 지시어로 소스 코드에 포함되어 사용되는 파일이다. - C++ - C++/CLI
C++/CLI는 .NET 환경에서 관리 코드와 네이티브 코드의 혼합 사용을 위해 C++ 언어를 확장한 것으로, .NET 객체 생성, 핸들, 추적 참조 등의 구문을 제공하며 C# 등 다른 .NET 언어와의 상호 운용성을 지원한다.
소멸자 (컴퓨터 프로그래밍) | |
---|---|
일반 정보 | |
이름 | 소멸자 |
프로그래밍 패러다임 | 객체 지향 프로그래밍 |
유형 | 서브루틴 |
호출 시점 | 객체 파괴 시 |
목적 | 객체에 할당된 리소스 해제 |
C++에서의 소멸자 | |
특징 | 클래스 이름 앞에 물결표(~)를 붙여 선언 매개변수를 가질 수 없음 반환 값을 가질 수 없음 오버로딩 불가 |
역할 | 객체가 더 이상 필요하지 않을 때 자동으로 호출 객체가 사용하는 리소스(메모리, 파일 핸들 등)를 해제 클래스에 동적 할당된 메모리가 있는 경우, 소멸자에서 해제해야 함 |
가상 소멸자 | 기반 클래스에 가상 소멸자를 선언하면, 파생 클래스의 소멸자가 올바르게 호출됨 다형성을 사용하는 경우, 가상 소멸자를 사용하는 것이 중요함 |
C#에서의 소멸자 | |
특징 | 클래스 이름 앞에 물결표(~)를 붙여 선언 매개변수를 가질 수 없음 접근 제어자를 사용할 수 없음 명시적으로 호출할 수 없음 |
역할 | 객체가 가비지 컬렉션에 의해 회수되기 직전에 호출 관리되지 않는 리소스(파일 핸들, 네트워크 연결 등)를 해제 대부분의 경우, 소멸자 대신 IDisposable 인터페이스를 구현하는 것이 좋음 |
Java에서의 소멸자 | |
특징 | finalize() 메서드를 사용하여 소멸자와 유사한 기능 제공 |
역할 | 객체가 가비지 컬렉션에 의해 회수되기 직전에 호출 더 이상 사용되지 않는 자원 반환 |
주의사항 | C++의 소멸자와 달리, finalize() 메서드가 언제 호출될지 예측하기 어려움 가능한 한 사용을 피하고, try-finally 블록이나 try-with-resources 구문을 사용하는 것이 좋음 |
기타 언어에서의 소멸자 | |
Python | __del__() 메서드를 사용하여 소멸자와 유사한 기능 제공 |
PHP | __destruct() 메서드를 사용하여 소멸자 정의 |
장점 | |
자원 관리 자동화 | 객체가 소멸될 때 자동으로 자원을 해제하여 자원 누수 방지 |
코드 간결성 | 명시적인 자원 해제 코드를 줄여 코드 가독성 향상 |
단점 | |
소멸 시점 제어 어려움 | 가비지 컬렉션 환경에서는 소멸 시점을 정확하게 예측하기 어려움 |
예외 처리 복잡성 | 소멸자 내에서 예외가 발생하면 프로그램 전체에 영향을 줄 수 있음 |
2. 소멸자 문법
다양한 프로그래밍 언어에서 소멸자는 각기 다른 문법으로 표현된다. 각 언어별 소멸자 문법은 다음과 같다.
프로그래밍 언어 | 소멸자 문법 | 비고 |
---|---|---|
C++ | 클래스 이름과 동일하지만 앞에 물결표(~)를 붙인다. (예: `~ClassName`)[2] | |
D | `~this()` | 생성자는 `this()`로 선언된다. |
오브젝트 파스칼 | `destructor` 키워드를 사용하며, 관례적으로 `Destroy`라는 이름을 사용한다. | |
오브젝티브-C | `dealloc` 메서드 | |
펄 | `DESTROY` 메서드 | Moose 객체 시스템 확장 사용 시 `DEMOLISH` 메서드 |
PHP (버전 5 이상) | `__destruct` 메서드[3] | 이전 버전에는 소멸자가 없다. |
파이썬 | `__del__` 메서드[4] | 파이썬 3에서는 종결자로 간주된다.[5] |
Rust | `drop` 메서드[6] | |
Swift | `deinit` 메서드 |
2. 1. C++
C++에서 소멸자는 클래스와 동일한 이름을 가지지만, 이름 앞에 물결표(~) 기호를 붙여 선언한다.[2] 예를 들어, `MyClass`라는 클래스의 소멸자는 `~MyClass()`가 된다. 소멸자는 매개변수를 가지거나 값을 반환하지 않는다.[2]객체의 수명이 다하면 소멸자가 자동으로 호출된다.[2] 객체가 자동 변수(스택에 생성된 지역 변수)로 선언된 경우, 해당 변수가 선언된 스코프를 벗어날 때 소멸자가 자동으로 호출된다. 만약 객체가 `new` 연산자를 사용하여 힙 메모리에 동적으로 할당되었다면, 해당 객체를 가리키는 포인터에 `delete` 연산자를 적용할 때 소멸자가 호출된다. C++에는 자동 가비지 컬렉션 기능이 없으므로, `new`로 생성된 객체는 반드시 `delete`를 통해 명시적으로 메모리를 해제하고 소멸자를 호출해야 한다. 이러한 `delete` 호출은 주로 스마트 포인터와 같은 자원 관리 클래스의 소멸자 내부에서 이루어진다.
상속 계층 구조에서는 가상 소멸자(virtual destructor)의 사용이 중요하다. 기본 클래스의 포인터를 통해 파생 클래스의 객체를 참조하고 삭제하는 경우(`delete base_ptr;`), 기본 클래스의 소멸자가 가상으로 선언되어 있지 않으면 기본 클래스의 소멸자만 호출되고 파생 클래스의 소멸자는 호출되지 않아 메모리 누수나 자원 해제 실패 등의 문제가 발생할 수 있다. 기본 클래스의 소멸자를 `virtual` 키워드를 사용하여 가상 소멸자로 선언하면, 포인터의 실제 대상 객체 타입(파생 클래스)의 소멸자가 먼저 호출된 후 기본 클래스의 소멸자가 호출되어 객체가 올바르게 소멸된다. 가상 소멸자는 가상 함수 테이블을 사용하므로 약간의 메모리 및 성능 오버헤드가 발생할 수 있지만, 다형성을 활용하는 클래스 설계에서는 필수적이다.
소멸자는 예외를 발생시키면 안 된다.[7][23] 프로그램 실행 중 예외가 발생하여 스택 되감기(stack unwinding)가 진행될 때 해당 스택 프레임의 지역 객체들의 소멸자가 호출될 수 있다. 만약 이때 소멸자 내부에서 또 다른 예외가 발생하면, C++ 런타임 시스템은 이를 처리하지 못하고 `std::terminate` 함수를 호출하여 프로그램을 강제로 종료시키기 때문이다.
생성자에서 시스템 자원(메모리, 파일 핸들, 네트워크 소켓 등)을 획득하고 소멸자에서 이를 해제하도록 클래스를 작성하는 RAII(Resource Acquisition Is Initialization) 패턴은 C++에서 자원 관리를 위한 중요한 기법이다. 자동 변수로 RAII 객체를 사용하면, 함수의 정상 종료 시점뿐만 아니라 예외 발생으로 인해 함수 중간에 빠져나가게 되더라도 객체의 소멸자가 반드시 호출되어 자원이 안전하게 해제됨을 보장할 수 있다.
만약 프로그래머가 클래스에 소멸자를 명시적으로 정의하지 않으면, C++ 컴파일러는 기본적인 소멸자를 암묵적으로 생성한다. 이 암묵적 소멸자는 아무런 동작도 하지 않으며, 가상(virtual)이 아니다.
다음은 간단한 클래스와 소멸자 사용 예시이다. `main` 함수가 종료될 때, 지역 변수 `obj`의 스코프가 끝나면서 `MyClass`의 소멸자 `~MyClass()`가 자동으로 호출된다.
```cpp
#include
class MyClass {
public:
// 생성자
MyClass() { std::cout << "MyClass() is called." << std::endl; }
// 소멸자
~MyClass() { std::cout << "~MyClass() is called." << std::endl; }
};
int main() {
std::cout << "main() function started." << std::endl;
MyClass obj; // main 함수 블록을 벗어날 때 obj의 소멸자가 호출된다.
std::cout << "main() function finished." << std::endl;
return 0;
}
```
클래스가 아닌 스칼라 타입(예: `int`, `double`, 포인터 타입 등)에 대해서도 명시적으로 소멸을 표현하기 위한 문법인 의사 소멸자(pseudo-destructor)가 존재한다. 이는 `typedef`나 템플릿 인수를 통해 얻은 타입 이름 뒤에 `.~타입이름()` 형태로 사용한다. 예를 들어, `int* p; using T = int*; p->~T();` 와 같이 쓸 수 있다. 이 구문은 해당 타입에 실제 소멸자가 있는지 여부와 관계없이 사용할 수 있으며, 템플릿 코드 등에서 일반성을 확보하기 위해 사용될 수 있다. C++ 표준의 초기 버전에서는 의사 소멸자가 아무런 효과도 가지지 않았으나, 이후 표준 개정을 통해 의사 소멸자가 호출된 객체의 수명을 종료시키는 것으로 의미가 변경되었다.[8]
2. 2. Object Pascal
Object Pascal에서는 소멸자는 키워드 'destructor'를 가지며 사용자 정의 이름을 가질 수 있지만, 대부분 'Destroy'로 명명된다.2. 3. Perl
Perl에서 소멸자 메서드의 이름은DESTROY
이다. Moose 객체 시스템 확장에서는 DEMOLISH
로 명명된다.2. 4. Python
파이썬 2 언어 가이드에서는 `__del__` 메서드를 소멸자라고 부르지만,[4] 파이썬 3에서는 실제로 종결자임을 인정한다.[5]2. 5. Objective-C
Objective-C에서 소멸자 메서드의 이름은 dealloc이다.2. 6. PHP
PHP 5 버전 이상에서 소멸자 메서드의 이름은 `__destruct
`이다. 이전 버전의 PHP에는 소멸자가 없었다.[3]2. 7. D
D에서는 소멸자를~this()
라는 이름으로 선언한다. 참고로 생성자는 this()
로 선언한다.2. 8. Rust
Rust의 소멸자 메서드의 이름은drop
이다.[6]2. 9. Swift
Swift에서는 소멸자 메서드의 이름이 `deinit`이다.2. 10. Xojo
Xojo (REALbasic)의 소멸자는 두 가지 형태 중 하나를 가질 수 있다. 각 형태는 매개변수와 반환값이 없는 특별한 이름의 일반 메서드 선언을 사용한다.- 이전 형태: 클래스와 동일한 이름에 물결표(~) 접두사를 사용하여 `~ClassName`과 같이 선언한다.
- 새로운 형태: `Destructor`라는 이름을 사용하여 선언한다.
새로운 형태는 클래스 리팩토링을 더 쉽게 만들어주기 때문에 선호된다.
```vb.net
Class Foobar
' 이전 형태
Sub ~Foobar()
End Sub
' 새로운 형태
Sub Destructor()
End Sub
End Class
3. C++에서의 소멸자
C++에서 소멸자는 객체의 수명이 종료될 때 자동으로 호출되는 특별한 멤버 함수이다.[2] 객체가 소멸될 때 필요한 마무리 작업, 특히 생성자에서 할당했던 자원(예: 동적으로 할당된 메모리, 열린 파일 핸들)을 해제하는 역할을 주로 담당한다.
소멸자의 이름은 해당 클래스의 이름 앞에 물결표(~) 기호를 붙여 만든다.[2] 예를 들어, `MyClass`라는 클래스의 소멸자는 `~MyClass()`가 된다. 소멸자는 매개변수를 가지지 않으며 반환 형식도 없다.[2]
소멸자가 호출되는 시점은 객체가 어떻게 생성되었는지에 따라 다르다.
- 자동 변수(함수 내에 선언된 지역 변수 등)로 생성된 객체는 해당 변수가 선언된 스코프를 벗어날 때 자동으로 소멸자가 호출된다.
- `new` 연산자를 사용하여 힙(heap)에 생성된 객체는, 해당 객체를 가리키는 포인터에 대해 `delete` 연산자가 호출될 때 소멸자가 호출된다. C++에는 가비지 컬렉션 기능이 없으므로, `new`로 생성한 객체는 반드시 프로그래머가 `delete`를 호출하여 명시적으로 파괴해야 한다. 종종 이러한 `delete` 호출은 스마트 포인터와 같은 관리 객체의 소멸자 내에서 자동으로 이루어지기도 한다.
상속 관계에서는 다형성을 위해 기본 클래스의 포인터로 파생 클래스 객체를 참조하는 경우가 많다. 이때 기본 클래스의 포인터를 통해 객체를 `delete`할 때 파생 클래스의 소멸자까지 올바르게 호출되도록 하려면, 기본 클래스의 소멸자를 가상 소멸자(`''virtual''` 키워드 사용)로 선언해야 한다. 만약 기본 클래스의 소멸자가 가상이 아니면, 기본 클래스의 소멸자만 호출되어 파생 클래스에서 할당한 자원이 해제되지 않는 메모리 누수 등의 문제가 발생할 수 있다.
소멸자는 RAII (Resource Acquisition Is Initialization)라는 중요한 C++ 프로그래밍 기법의 핵심 요소이다. RAII는 생성자에서 자원을 획득하고 소멸자에서 해당 자원을 해제하도록 클래스를 설계하는 방식으로, 예외가 발생하여 정상적인 흐름을 벗어나더라도 소멸자가 호출되어 자원이 안전하게 해제되도록 보장한다.
주의할 점은 소멸자 내부에서 예외를 던져서는 안 된다는 것이다.[7][23] 만약 예외 전파 과정 중에 호출된 소멸자가 또 다른 예외를 던지게 되면, C++ 표준에 따라 프로그램 실행이 비정상적으로 종료(`std::terminate` 호출)될 수 있다.
만약 프로그래머가 클래스에 소멸자를 명시적으로 정의하지 않으면, C++ 컴파일러는 기본적인 소멸자를 암묵적으로 생성한다. 이 암묵적 소멸자는 내용이 비어 있으며 가상(non-virtual)이 아니다.
또한 C++에는 스칼라 타입(예: `int`, `float`, 포인터 등)에 대해서도 소멸자와 유사한 문법을 사용할 수 있는 의사 소멸자(pseudo-destructor) 개념이 존재한다. 이는 템플릿 코드 등에서 타입에 관계없이 일관된 방식으로 객체의 수명 종료를 명시하는 데 사용될 수 있다.[8]
3. 1. C++ 소멸자 예시
C++에서 소멸자는 클래스와 이름은 같지만, 이름 앞에 물결표 (~) 기호를 붙여 구분한다.[2] 예를 들어, `MyClass`라는 클래스의 소멸자는 `~MyClass()`가 된다. 소멸자는 매개변수나 반환 형식을 가지지 않는다.[2]소멸자는 객체의 수명이 끝날 때 자동으로 호출된다.[2] 객체가 자동 변수(지역 변수)로 생성되었다면, 해당 변수가 선언된 블록을 벗어날 때 소멸자가 호출된다. 만약 객체가 `new` 연산자를 사용하여 힙에 생성되었다면, `delete` 연산자를 해당 객체를 가리키는 포인터에 적용할 때 소멸자가 호출된다. C++에는 가비지 컬렉션이 없으므로, 프로그래머가 직접 `delete`를 호출하여 메모리를 해제하고 소멸자를 호출해야 한다. 이러한 `delete` 호출은 종종 스마트 포인터와 같은 다른 객체의 소멸자 내부에서 이루어진다.
소멸자는 주로 생성자에서 획득한 자원(메모리, 파일 핸들 등)을 해제하는 데 사용된다. 다음은 동적 할당된 메모리를 해제하는 소멸자의 예시이다.
#include
#include
#include
class Foo {
public:
// 생성자: "Hello, World!" 문자열을 저장할 메모리를 동적으로 할당하고 복사한다.
Foo(): data_(new char[sizeof("Hello, World!")]) { // 문자열 길이에 맞는 메모리 할당
std::strcpy(data_, "Hello, World!"); // 할당된 메모리에 문자열 복사
}
// 복사 생성자 비활성화 (C++11 이상): 객체 복사를 막는다.
// 포인터 멤버가 있는 클래스에서 복사 생성자를 제대로 구현하지 않으면
// 얕은 복사 문제가 발생할 수 있으므로, 복사가 필요 없다면 비활성화하는 것이 안전하다.
Foo(const Foo& other) = delete;
// 대입 연산자 비활성화 (C++11 이상): 객체 대입을 막는다.
// 복사 생성자와 마찬가지 이유로 비활성화한다.
Foo& operator=(const Foo& other) = delete;
// 소멸자: 생성자에서 할당한 메모리를 해제한다.
~Foo(void) {
delete[] data_; // 'new char[]'로 할당했으므로 'delete[]'로 해제해야 한다.
}
private:
// friend 함수 선언: 외부 함수 operator<<가 Foo 클래스의 private 멤버 data_에 접근할 수 있도록 허용한다.
friend std::ostream& operator<<(std::ostream& os, const Foo& foo);
char* data_; // 동적으로 할당된 문자열을 가리키는 포인터 멤버 변수
};
// friend로 선언된 외부 함수 정의: Foo 객체를 출력 스트림에 출력하는 방법을 정의한다.
std::ostream& operator<<(std::ostream& os, const Foo& foo) {
os << foo.data_; // private 멤버인 data_에 접근하여 출력
return os;
}
int main() {
Foo foo; // 자동 변수로 Foo 객체 생성. 생성자 호출됨.
std::cout << foo << std::endl; // 오버로딩된 << 연산자를 통해 객체의 내용("Hello, World!") 출력
} // main 함수 종료 시 자동 변수 foo의 스코프를 벗어나므로 소멸자 ~Foo()가 자동으로 호출되어 메모리가 해제됨.
위 예제에서 `Foo` 객체 `foo`는 `main` 함수의 자동 변수이다. `main` 함수가 종료될 때 `foo` 객체의 수명이 다하므로, 소멸자 `~Foo()`가 자동으로 호출되어 `new`로 할당했던 메모리가 `delete[]`를 통해 해제된다. 만약 객체를 안전하게 복사하거나 대입할 수 없다면, 위 예제처럼 C++11의 `delete` 키워드를 사용하여 복사 생성자와 대입 연산자를 명시적으로 비활성화하는 것이 좋다. 이는 Scott Meyers의 저서 ''Effective Modern C++''에서도 권장하는 방법이다.[9]
다음 예제는 소멸자가 언제 호출되는지 명확하게 보여준다.
#include
class MyClass {
public:
// 기본 생성자
MyClass() { std::cout << "MyClass() is called." << std::endl; }
// 소멸자
~MyClass() { std::cout << "~MyClass() is called." << std::endl; }
};
int main() {
std::cout << "main() function started." << std::endl;
MyClass obj; // 자동 변수 obj 생성. 생성자 호출됨.
std::cout << "main() function finished." << std::endl;
} // main 함수 블록을 빠져나가면서 자동 변수 obj가 파괴되고, 소멸자 ~MyClass()가 호출됨.
상속 관계에서는 다형성을 위해 기본 클래스의 포인터로 파생 클래스 객체를 다루는 경우가 많다. 이때 기본 클래스의 포인터를 통해 파생 클래스 객체를 `delete`하면, 기본 클래스의 소멸자만 호출될 수 있다. 이를 방지하고 파생 클래스의 소멸자까지 올바르게 호출되게 하려면 기본 클래스의 소멸자를 가상 소멸자 (`virtual`)로 선언해야 한다. 가상 소멸자를 사용하면 객체 크기가 약간 증가하고 호출 오버헤드가 발생할 수 있지만, 메모리 누수와 같은 심각한 문제를 예방할 수 있다.
소멸자는 예외가 발생하여 블록을 벗어날 때도 호출된다. 이를 이용하여 생성자에서 자원을 획득하고 소멸자에서 자원을 해제하도록 클래스를 설계하면, 예외 발생 여부와 관계없이 자원 해제가 보장된다. 이러한 기법을 RAII (Resource Acquisition Is Initialization)라고 한다.
주의할 점은 소멸자 내부에서 예외를 던져서는 안 된다는 것이다.[7] 만약 예외 처리 과정 중에 호출된 소멸자가 또 다른 예외를 던지면, C++ 표준에 따라 프로그램이 즉시 종료될 수 있다 (`std::terminate` 호출).
만약 프로그래머가 소멸자를 직접 정의하지 않으면, 컴파일러는 비어있고 가상이 아닌 (non-virtual) 소멸자를 암묵적으로 생성한다.
C++에는 비클래스 스칼라 타입(예: `int`, `float`, 포인터 등)에 대해서도 소멸자 호출과 유사한 문법을 사용할 수 있는데, 이를 의사 소멸자(pseudo-destructor)라고 한다. 이는 템플릿 코드 등에서 타입을 알 수 없을 때 유용하게 사용될 수 있다.
int f() {
int a = 123;
using T = int;
a.~T(); // int 타입 변수 a에 대해 의사 소멸자 호출
// C++ 표준 변경 이후, 이 호출은 변수 a의 수명을 종료시킨다. [8]
// 따라서 이후 a를 사용하는 것은 정의되지 않은 동작(undefined behavior)이다.
return a;
}
과거 표준에서는 의사 소멸자가 아무 효과가 없었으나, 이후 표준 개정을 통해 해당 객체의 수명을 종료시키는 것으로 변경되었다.[8]
4. C with GCC extensions
5. 파이널라이저 (Finalizer)
'''파이널라이저''' (finalizer)는 가비지 컬렉터를 가진 언어에서 더 이상 사용되지 않는 객체가 메모리에서 회수되기 전에 자동으로 호출되는 메서드이다.
주로 Java, Ruby 같은 언어에서 사용된다. C++/CLI에는 소멸자와 파이널라이저가 모두 존재하며, C#에는 파이널라이저만 있지만 문법이 C++의 소멸자와 매우 유사하여 한때 "소멸자"라고 불리기도 했다.[13][14]
소멸자와 달리, 파이널라이저는 객체가 불필요해진 시점에 바로 호출되지 않을 수 있다는 중요한 차이점이 있다. 객체가 불필요해진 후 실제로 메모리에서 회수되기 전 언젠가 호출될 뿐이며, 그 시점은 예측하기 어렵다. 심지어 메모리에 여유가 많다면 객체가 회수되지 않아 파이널라이저가 영원히 호출되지 않을 수도 있다. 예를 들어 .NET Framework에서는 응용 프로그램이 종료될 때 파이널라이저가 호출되지만, 일반적인 프로세스 종료 시에도 호출이 보장되는 것은 아니다.
이처럼 언제 호출될지, 심지어 호출될지조차 불확실하기 때문에, 반드시 실행되어야 하는 중요한 처리를 파이널라이저에 맡기는 것은 위험하다. 따라서 RAII(Resource Acquisition Is Initialization) 패턴을 파이널라이저로 구현하려는 시도는 적절하지 않다. 파이널라이저의 역할은 매우 제한적으로, 주로 "혹시 리소스 해제를 잊었는지 확인하고, 잊었다면 마지막으로 해제하는" 최종 방어벽 정도로 사용될 수 있다. 리소스는 사용이 끝난 시점에 즉시, 명시적으로 해제하는 것이 원칙이며, 파이널라이저의 확인은 어디까지나 보험일 뿐이다. 리소스 해제를 잊는 것 자체가 버그이므로, 파이널라이저에 의존하는 설계는 피해야 한다. 파이널라이저를 잘못 구현하면 취약성이 발생할 수도 있다.[15]
이러한 불확실성 외에도 파이널라이저 사용은 가비지 컬렉터의 성능을 저하시키는 요인이 될 수 있어 적극적으로 권장되지 않는다. 파이널라이저를 가진 객체는 별도의 관리 목록에 등록되어 처리되므로, 그렇지 않은 객체에 비해 추가적인 오버헤드가 발생한다.[16] 다만, 약간의 버그가 있더라도 지속적으로 작동해야 하는 업무용 서버 같은 환경에서는 예외적으로 사용되는 경우도 있다.
Java 9 버전부터는 파이널라이저 사용이 공식적으로 권장되지 않는다.[17]
6. 다른 언어의 유사 기능
소멸자에 의한 RAII와 유사한 기능을 구현하는 방법은 언어마다 존재한다.
- 자바: `AutoCloseable` 인터페이스를 구현하고 `try-with-resources` 문을 사용한다.
- C#: `System.IDisposable` 인터페이스를 구현하고 `using` 문을 사용한다.
- C++/CLI: 관리형 타입에 소멸자를 정의하면 `IDisposable` 인터페이스를 암묵적으로 구현하게 된다.[18]
- Objective-C: 인스턴스의 참조 셈이 0이 되면 런타임에 의해 `dealloc` 메서드가 자동으로 호출된다.[19] 이 메서드 내부에 Objective-C의 관리 범위 밖에 있는 자원을 해제하는 등의 정리 코드를 작성할 수 있다.
- Swift: deinitializer인 `deinit` 키워드를 사용한다.[20]
참조
[1]
뉴스
dtor
https://acronyms.the[...]
2018-10-14
[2]
서적
Concepts of Programming Languages
https://www.pearson.[...]
Addison-Wesley
2012
[3]
문서
Constructors and Destructors
http://www.php.net/m[...]
[4]
웹사이트
3. Data model — Python 2.7.18 documentation
https://docs.python.[...]
[5]
웹사이트
3. Data model — Python 3.10.4 documentation
https://docs.python.[...]
[6]
웹사이트
Destructors - the Rust Reference
https://doc.rust-lan[...]
[7]
문서
GotW #47: Uncaught exceptions
http://www.gotw.ca/g[...]
2011-07-31
[8]
웹사이트
P0593R6:Implicit creation of objects for low-level object manipulation
https://www.open-std[...]
2022-11-25
[9]
서적
Effective Modern C++
O'REILLY
[10]
문서
C "destructor" function attribute
https://gcc.gnu.org/[...]
[11]
서적
Hacking the art of exploitation
No Starch Press
[12]
문서
デストラクタ(解体子)とは - IT用語辞典 e-Words
http://e-words.jp/w/[...]
[13]
문서
Destructors (C# Programming Guide) | Microsoft Docs
https://docs.microso[...]
[14]
문서
Finalizers (C# Programming Guide) | Microsoft Docs
https://docs.microso[...]
[15]
문서
ヒント: ファイナライザーによる脆弱性からコードを保護する | IBM
https://web.archive.[...]
Internet Archive
[16]
문서
ファイナライザを理解する ~ファイナライザに起因するトラブルを避けるために~ | 富士通 | 橋口 雅史
https://www.fujitsu.[...]
[17]
문서
Object (Java SE 9 & JDK 9 )
https://docs.oracle.[...]
[18]
문서
How to: Define and consume classes and structs (C++/CLI) | Microsoft Learn
https://learn.micros[...]
[19]
문서
dealloc | Apple Developer Documentation
https://developer.ap[...]
[20]
문서
Deinitialization | Documentation
https://docs.swift.o[...]
[21]
문서
https://docs.python.org/2/reference/datamodel.html#object.
https://docs.python.[...]
[22]
문서
Constructors and Destructors
http://www.php.net/m[...]
[23]
문서
GotW #47: Uncaught exceptions
http://www.gotw.ca/g[...]
2011-07-31
[24]
서적
Effective C++
Addison-Wesley
[25]
서적
Hacking the art of exploitation
https://archive.org/[...]
No Starch Press
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com