rein's world

C++ 객체에 (흔히) Initialize/Finalize 같은 함수가 있는 이유

KLDP 쪽에 관련 스레드가 있길래 든 생각.

안녕하세요.

문득 궁금한 점이 생겼습니다.

C++에서 객체에대한 초기화(Initialize) 나 해제(Finalize) 메소드를 구현하는 이유는 뭘까요?

얼핏 생각해보면 그냥 생성자나 소멸자를 이용해도 좋을 것 같은데…

물론 정의하고 구현하기 나름이겠지만..

혹시 반드시 이렇게 클래스를 정의할 필요가 있거나,  이런 방식으로 객체를 보다 효율적으로 관리할 수 있는 특수한 경우가 있나요?

궁금합니다.

라는 질문이 올라왔다. 이중 Initialize() 함수에 관한 답변들이,

  • 자원 할당 해제
  • 자유 공간(free space)에 할당/해제하는 부담 때문에

등등인데 이건 좀.

 

C++ 이 초기에 만들어진 OO 언어이기 때문에1 객체 생성 과정이 좀 특이하다. Java/C#의 객체 생성을 아는 분은 좀 더 이해하기 쉽겠지만 그 부분은 생략.

class A, B, C 가 있는데 이게 B는 A를 상속받고, C는 B를 상속 받았다고 했을 때, C 객체를 생성하면,

  1. A의 생성자가 완료되고
  2. B의 생성자가 완료되고
  3. C의 생성자가 완료된다

근데 중요한 것은 이 각 단계마다 각 객체의 형(type)이 변화한다. 1에서는 A 타입이고 2에선 B 타입인 것. 물론 같은 메모리를 쓰는 같은 객체다. 초기화가 안 끝났을 뿐.

그래서 1에서 virtual 함수를 호출하면 이게 실제론 C 타입 객체를 만들고 있는 과정이지만 A의 virtual 함수가 호출 된다.  그리고 A 가 pure-virtual 클래스라거나 하면 pure virtual function call 예외를 보게 된다. 그런 이유에서 생성자에서 다형성을 쓰거나, 객체의 초기화 시점을 손으로 결정하려는 경우 등에서 Initialize 함수를 쓰게 된다.

덤으로 Sutter가 언급한 이런 경우도 있다: Constructor Exceptions in C++, C#, and Java

이런 경우엔 _ 완전히 생성된 객체가 아니라서 소멸자가 호출될 수 없다_. 그래서 일단 객체를 만들고 초기화하고 dtor/disposer에서 처리되게 만들 수 있다.  

Finalize 함수를 쓰는 이유는 그나마 좀 짚어놨는데, 소멸자에서 예외가 생기면 처리가 불가능하다. 소멸자 안에 처리되지 않은 예외가 있으면 프로그램이 강제 종료되기에; 그래서 프로토타이핑한다거나 할 때는 Finalize 따위가 꽤나 유용하다. 정리 시점과 메모리 반환 시점을 따로 할 때도 필요하다.2

그리고 객체 소멸이란게 메모리 할당의 해제 / 내부에 잡고 있는 자원의 해제 두 가지라서 이를 별도로 구현하는 C# 같은 언어도 있다.

ps. C++ 은 이미 너무 방대해진 언어라 C#/Java 같은 주석서(?)가 필요한 건지도 모르겠다;


  1. 물론 C++ 자체야 복합적인 이러저런 패러다임을 가지고 있는 언어다. ↩︎

  2. 하지만 대부분의 경우 RAII idiom 으로 돌기 때문에 꼭 이런 패턴이 자주나오진 않는다. ↩︎