Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++ 프라이빗 소멸자 완전 정복: 동작 원리부터 활용법까지


C++에서 클래스의 소멸자(destructor)private 멤버로 선언하면 어떤 일이 일어날까요? 이 글에서는 여러 예제 코드를 통해 프라이빗 소멸자가 컴파일 과정과 객체의 생성·소멸에 어떤 영향을 미치는지 단계별로 살펴보겠습니다.

1. 객체를 생성하지 않는 경우

다음 코드에는 private 소멸자가 정의되어 있지만, 실제 객체를 생성하지 않으므로 어떠한 오류도 발생하지 않습니다.

예제 1

#include <iostream>
using namespace std;
class my_class {
    private:
        ~my_class() {
            //private 소멸자
        }
};
int main() {
}

2. 스택에 객체를 생성하는 경우

반면, 아래 코드처럼 스택(stack)에 객체를 생성하려고 하면 컴파일 오류가 발생합니다. 컴파일러는 소멸자에 접근할 수 없다는 사실을 감지하고, 함수가 종료될 때 객체를 자동으로 파괴할 수 없기 때문입니다.

예제 2

#include <iostream>
using namespace std;
class my_class {
    private:
        ~my_class() {
            //private 소멸자
        }
};
int main() {
    my_class obj;
}

출력 결과

[Error] 'my_class::~my_class()' is private

3. 포인터만 선언하는 경우

클래스 타입의 포인터만 선언하면 오류가 발생하지 않습니다. 포인터 변수는 실제 객체를 생성하는 것이 아니라 메모리 주소를 저장할 공간만 마련하기 때문입니다.

예제 3

#include <iostream>
using namespace std;
class my_class {
    private:
        ~my_class() {
            //private 소멸자
        }
};
int main() {
    my_class *obj;
}

4. new 연산자로 객체를 생성하는 경우

new 연산자를 사용해 힙(heap)에 객체를 생성해도 역시 오류가 발생하지 않습니다. 이 경우 컴파일러는 객체의 삭제(delete)를 프로그래머의 책임으로 간주하기 때문입니다.

예제 4

#include <iostream>
using namespace std;
class my_class {
    private:
        ~my_class() {
            //private 소멸자
        }
};
int main() {
    my_class *obj = new my_class;
}

5. delete 문을 사용하는 경우

그런데 여기에 delete 문을 추가해 객체를 삭제하려고 하면, 소멸자가 private이므로 컴파일 오류가 발생합니다.

예제 5

#include <iostream>
using namespace std;
class my_class {
    private:
        ~my_class() {
            //private 소멸자
        }
};
int main() {
    my_class *obj = new my_class;
    delete obj;
}

출력 결과

[Error] 'my_class::~my_class()' is private

6. 프라이빗 소멸자는 언제 사용할까?

프라이빗 소멸자는 단순히 접근을 차단하는 용도 외에도 실무에서 유용하게 활용됩니다. 대표적인 예가 힙 전용(heap-only) 객체 패턴입니다. 소멸자를 private으로 선언하면 스택 할당이 불가능해지므로, 개발자가 반드시 new를 통해서만 객체를 생성하도록 강제할 수 있습니다.

이 경우 객체의 해제는 클래스 내부의 public 정적(static) 멤버 함수나 friend 함수를 통해 제공해야 합니다.

예제 6: 정적 함수로 안전하게 삭제하기

#include <iostream>
using namespace std;

class my_class {
private:
    ~my_class() {
        cout << "소멸자 호출" << endl;
    }
public:
    static void destroy(my_class* ptr) {
        delete ptr;   // 클래스 내부이므로 private 소멸자 접근 가능
    }
};

int main() {
    my_class *obj = new my_class;
    my_class::destroy(obj);  // 정상적으로 소멸자 호출됨
    return 0;
}

출력 결과

소멸자 호출

7. 핵심 정리

  • private 소멸자가 있어도 객체를 생성하지 않으면 컴파일 오류가 발생하지 않습니다.
  • 스택에 객체를 생성하면 컴파일러가 소멸자 접근 불가를 감지하여 오류가 발생합니다.
  • 포인터 선언이나 new를 통한 힙 할당은 오류 없이 가능합니다.
  • 단, delete로 객체를 해제하려 하면 소멸자가 private이므로 오류가 발생합니다.
  • 따라서 private 소멸자를 사용할 때는 클래스 내부의 정적 함수나 friend 함수를 통해 해제 경로를 반드시 제공해야 합니다.