KLDP 쪽에 관련 스레드가 있길래 든 생각.
안녕하세요.
문득 궁금한 점이 생겼습니다.
C++에서 객체에대한 초기화(Initialize) 나 해제(Finalize) 메소드를 구현하는 이유는 뭘까요?
얼핏 생각해보면 그냥 생성자나 소멸자를 이용해도 좋을 것 같은데…
물론 정의하고 구현하기 나름이겠지만..
혹시 반드시 이렇게 클래스를 정의할 필요가 있거나,
이런 방식으로 객체를 보다 효율적으로 관리할 수 있는 특수한 경우가 있나요?궁금합니다.
라는 질문이 올라왔다. 이중 Initialize() 함수에 관한 답변들이,
- 자원 할당 해제
- 자유 공간(free space)에 할당/해제하는 부담 때문에
등등인데 이건 좀(…).
C++ 이 초기에 만들어진 OO 언어이기 때문에 ((물론 C++ 자체야 복합적인 이러저런 패러다임을 가지고 있는 언어니까 그 얘기는 귀찮게 더 안하겠다)) 객체 생성 과정이 좀 특이하다. Java/C#의 객체 생성을 아는 분은 좀 더 이해하기 쉬울텐데 그 설명은 생략(…).
class A, B, C 가 있는데 이게 B는 A를 상속받고, C는 B를 상속 받았다고 했을 때, C 객체를 생성하면,
- A의 생성자가 완료되고
- B의 생성자가 완료되고
- 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 따위가 꽤나 유용하다. 정리 시점과 메모리 반환 시점을 따로 할 때도 나름대로 쓸모 있고… ((하지만 대부분의 경우 RAII idiom 으로 돌기 때문에 꼭 이런 패턴이 자주나오는건 아니다…))
그리고 객체 소멸이란게 메모리 할당의 해제 / 내부에 잡고 있는 자원의 해제 두 가지라서 이를 별도로 구현하는 C# 같은 애들도 있고(…).
ps. C++ 은 이미 너무 방대해진 언어라 C#/Java 같은 주석서(?)가 필요한 건지도 모르겠다;
C#/Java 를 주석서에 비유하는건 꽤나 그럴싸… 낄낄
후후(…)
생성자는 예상했던 대로인데 소멸자는 못맞췄음. ㄲㄲㄲ
소멸자는 너무나도 C++적인 이유…
오..
검색으로 들어와서
희미하게 알고 있던 걸 바로잡고 갑니다
감사합니다~
검색으로 여기를 찾아오다니! 선검색(???)