프로그래밍/C\C++
[Effective C++ 정리 #19] 클래스 설계는 타입 설계로 접근하라
허구의
2025. 7. 9. 07:21
728x90
[Effective C++ 정리 #19] 클래스는 타입이다
C++에서 class
를 만든다는 것은 단순히 데이터와 함수들을 묶는 것이 아닙니다. class
는 곧 새로운 타입(type)을 정의하는 행위이며, 우리는 타입 설계자(type designer)가 됩니다.
해당 아이템에서는 C++ 클래스 설계를 '타입 시스템 강화'라는 관점에서 바라보며, 좋은 타입을 설계하기 위해 꼭 고민해야 할 10가지 질문을 제시합니다.
좋은 타입이란 어떤 것인가?
좋은 타입은 다음과 같은 특징을 갖습니다:
- 자연스러운 문법: 직관적인 인터페이스로 사용자가 쉽게 사용할 수 있어야 함
- 예측 가능한 의미: 어떤 연산이 어떤 결과를 만드는지 명확하게 알 수 있어야 함
- 효율적인 구현: 지나치게 무겁지 않고, 가능한 한 가볍고 빠르게 동작해야 함
반면, 잘못 설계된 클래스는 사용하기 어렵고, 성능도 떨어지며, 의미적으로도 혼란을 줍니다.
클래스를 설계할 때 반드시 자문해야 할 10가지 질문
- 객체는 어떻게 생성되고 소멸되어야 하는가?
:생성자, 소멸자,operator new/delete
를 포함한 메모리 관리 방식 결정이 필요합니다. - 초기화와 대입은 어떻게 구분해야 하는가?
:생성자와 대입 연산자는 별개의 책임을 가지며, 혼동하지 않도록 해야 합니다. - 값에 의한 전달은 어떤 의미를 가져야 하는가?
:복사 생성자를 통해 정의되며, 복사 비용과 의미를 명확히 해야 합니다. - 유효한 상태란 무엇인가?
:어떤 조합의 멤버 값이 유효한지를 정의하고, 이를 보장하기 위한 불변 조건(invariants)을 유지해야 합니다. - 상속 계층에 포함될 것인가?
:상속을 고려할 경우 가상 함수 선언 여부, 기초 클래스의 설계 제약 등을 함께 고려해야 합니다. - 타입 간 변환은 허용할 것인가?
:암시적 변환(implicit)과 명시적 변환(explicit)을 구분하여 정의해야 하며, 혼란을 줄이기 위한 정책이 필요합니다. - 어떤 연산자나 함수가 이 타입에 적절한가?
:연산자 오버로딩은 직관성과 의미 일관성을 고려해야 하며, 꼭 필요한 것만 제공해야 합니다. - 어떤 기본 함수는 금지해야 하는가?
:예를 들어, 복사를 금지하려면 복사 생성자와 대입 연산자를 private로 선언하거나 delete 처리합니다. - 누가 멤버에 접근할 수 있어야 하는가?
:public, protected, private 접근 제어는 물론 friend 선언도 함께 설계에 포함되어야 합니다. - 암묵적인 인터페이스는 무엇인가?
:예외 안전성, 자원 해제, 성능 보장 등 인터페이스에 명시되지 않은 특성들도 중요한 계약이 됩니다.
단일 클래스가 아니라 템플릿 클래스가 더 적절할 수도 있습니다
설계하려는 타입이 다양한 타입과 함께 쓰여야 하거나 일반적인 연산 패턴을 공유한다면, 클래스 템플릿을 고려하는 것이 더 나은 선택일 수 있습니다.
예: Stack<T>
, Optional<T>
, Result<T, E>
등
정말 새로운 타입이 필요한가?
단지 기존 클래스에 기능을 추가하고 싶은 경우라면, 굳이 파생 클래스를 만들지 않고 비멤버 함수(non-member function)나 템플릿 함수로 해결하는 것이 더 바람직할 수 있습니다.
예: to_string(const YourType&)
또는 hash<T>
처럼 기존 타입의 기능 확장을 외부에서 처리
핵심 요약
- 클래스를 정의한다는 것은 새로운 타입을 설계하는 일입니다.
감사합니다.
728x90