소멸자(Destructor)는 C++에서 클래스의 객체를 소멸(제거)하는 역할을 담당하는 특수 멤버 함수입니다. 객체의 수명이 끝나는 순간 자동으로 호출되며, 메모리 해제나 리소스 정리 작업에 주로 활용됩니다.
소멸자가 호출되는 시점
소멸자는 프로그램 내에서 객체가 자신의 유효 범위(scope)를 벗어나는 순간 호출됩니다. 대표적인 경우는 다음과 같습니다.
함수의 실행이 종료되어 함수의 범위를 벗어날 때
프로그램 전체가 종료될 때
객체의 지역 변수를 담고 있던 블록({ })이 범위를 벗어날 때
delete연산자로 동적으로 생성된 객체가 삭제될 때
예제 1: 값(value)을 반환하는 경우
먼저 아래 코드를 보고 출력 결과를 한번 예측해 보세요.
#include <iostream>
using namespace std;
int i;
class destructor {
public:
~destructor(){
i=10;
}
};
int valueInitializer() {
i=3;
destructor d1;
return i;
}
int main() {
cout<<"i = "<<valueInitializer()<<endl;
return 0;
}출력 결과
i = 3
코드를 살펴보면, 먼저 전역 변수 i를 선언하고 valueInitializer() 함수 안에서 그 값을 변경합니다. 함수 내부에서 i를 3으로 초기화한 뒤 객체 d1을 생성하고, 마지막으로 i의 값을 반환합니다.
정답은 3입니다. 맞히셨다면 잘하셨고, 10이라고 생각하셨다면 아래 설명을 주목해 주세요.
핵심은 호출 순서입니다. 앞서 말했듯이 소멸자는 함수가 범위를 벗어나는 시점에 호출됩니다. 그런데 그 시점보다 먼저, 함수는 이미 반환값 3을 복사하여 호출자에게 넘겨준 상태입니다. 즉, 소멸자가 i를 10으로 바꾸기 전에 값 3이 이미 복사되어 전달된 것이죠. 따라서 출력 결과는 3이 됩니다.
예제 2: 참조(reference)를 반환하는 경우
이번에는 코드를 조금만 바꿔서 다시 한번 출력 결과를 예측해 보세요.
#include <iostream>
using namespace std;
int i;
class destructor {
public:
~destructor(){
i=10;
}
};
int& valueInitializer() {
i=3;
destructor d1;
return i;
}
int main() {
cout<<"i = "<<valueInitializer()<<endl;
return 0;
}출력 결과
얼렁뚱땅 비슷해 보이는 코드지만, 이번에는 결과가 다릅니다.
출력 : i = 10
왜일까요?
차이점은 반환 방식에 있습니다. 이번에는 값을 그대로 반환하는 것이 아니라 변수의 참조(reference)를 반환했습니다. 참조는 원본 변수 그 자체를 가리키므로, 함수가 범위를 벗어나며 소멸자가 실행되어 i를 10으로 변경하면, 그 이후에 실제로 출력되는 값도 변경된 10이 되는 것입니다.
정리
소멸자는 객체가 범위를 벗어날 때 자동으로 호출됩니다.
값 반환: 반환 시점에 값이 복사되므로, 이후 소멸자가 변수를 변경해도 출력에는 영향을 주지 않습니다.
참조 반환: 원본 변수를 가리키므로, 소멸자에서 변경한 최종 값이 그대로 반영됩니다.
이처럼 반환 방식 하나만 달라져도 소멸자와의 상호작용 결과가 완전히 달라질 수 있습니다. 참조 반환은 지역 변수의 참조를 반환할 때 댕글링 참조(dangling reference) 위험도 있으니, 항상 객체의 수명과 호출 순서를 신중하게 고려하는 것이 좋습니다.