생성자에서 작업하기 (Doing Work in Constructors)¶
생성자에서 가상 메서드 호출을 피하고, 오류 신호를 보낼 수 없다면 실패할 수 있는 초기화를 피하세요.
정의:
생성자 본문에서는 임의의 초기화를 수행할 수 있습니다.
장점:
- 클래스가 초기화되었는지 여부를 걱정할 필요가 없습니다.
- 생성자 호출로 완전히 초기화되는 객체는
const가 될 수 있고, 표준 컨테이너나 알고리즘과 함께 사용하기도 더 쉬울 수 있습니다.
단점:
- 그 작업이 가상 함수를 호출한다면, 이 호출은 하위 클래스의 구현으로 전달되지 않습니다. 지금은 클래스에 하위 클래스가 없더라도 나중에 클래스를 수정하면서 이 문제가 조용히 끼어들어 큰 혼란을 일으킬 수 있습니다.
- 프로그램을 크래시시키거나(항상 적절하지는 않습니다) 예외를 사용하는(금지되어 있습니다) 방법 외에는, 생성자가 오류를 알릴 쉬운 방법이 없습니다.
- 그 작업이 실패하면 초기화 코드가 실패한 객체가 남습니다. 이는 호출하기를 잊기 쉬운
bool IsValid()같은 상태 확인 장치가 필요한 비정상적인 상태일 수 있습니다. - 생성자의 주소는 얻을 수 없으므로, 생성자에서 수행하는 작업은 예를 들어 다른 스레드로 넘기기가 쉽지 않습니다.
결정:
생성자는 절대 가상 함수를 호출해서는 안 됩니다. 코드에 적절하다면 프로그램을 종료하는 것이 알맞은 오류 처리 방법일 수 있습니다. 그렇지 않다면 TotW #42에 설명된 대로 팩토리 함수나 Init() 메서드를 고려하세요. 어떤 공개 메서드를 호출할 수 있는지를 좌우하는 다른 상태가 없는 객체에는 Init() 메서드를 두지 마세요(이런 형태의 반쯤 생성된 객체는 특히 올바르게 다루기 어렵습니다).
옮긴이 풀이¶
핵심: 생성자에서 "실패할 수 있는 일"과 "가상 함수 호출"을 피하라¶
생성자는 두 가지를 잘 못합니다.
- 오류 신호: 생성자는 값을 반환하지 않습니다. 따라서 초기화가 실패해도 예외(이 프로젝트에서는 금지)나 프로그램 종료 외에는 호출자에게 알릴 깔끔한 방법이 없습니다. 실패 가능한 초기화를 생성자에 넣으면 "반쯤 만들어진 객체"가 생기고, 이를 거르려고
bool IsValid()같은 확인 장치를 두게 되는데 호출을 잊기 쉽습니다. - 가상 함수 디스패치: 생성자 안에서 가상 함수를 호출하면 하위 클래스의 재정의가 호출되지 않습니다(그 시점엔 아직 하위 객체가 만들어지지 않았으므로). 지금은 하위 클래스가 없어도 나중에 생기면 조용히 잘못 동작합니다.
대안¶
- 실패할 수 있는 초기화가 필요하면 → 팩토리 함수나 별도의
Init()메서드를 쓰세요. - 단,
Init()의 성공 여부로 갈리는 "유효/무효" 외에 다른 상태가 없는 객체라면Init()패턴을 쓰지 마세요. 반쯤 초기화된 객체는 다루기 까다롭습니다. - 코드에 적절하다면 그냥 프로그램을 종료(crash)하는 것도 올바른 오류 처리일 수 있습니다.