[Effective C++] 항목19 : 클래스 설계는 타입 설계와 똑같이 취급하자

Jangmanbo·2023년 4월 9일
0

Effective C++

목록 보기
19/33

새로운 클래스를 정의한다는 것은 새로운 타입을 정의하는 것과 같다.
우리는 클래스 설계자에 그치지 않고 타입 설계자라는 막강한 권위를 가지고 있다는 것이다.

따라서 클래스를 설계할 때는 마치 언어 설계자가 그 언어의 기본제공 타입을 설계하면서 들인 것과 같은 정성과 보살핌이 필요하다.

좋은 클래스 설계하기

  1. 문법은 자연스럽게
  2. 의미구조는 직관적으로
  3. 효율적으로 구현

충분한 고민이 없다면 이 세 가지 중 하나도 달성하기 어렵다.


우리가 직면하게 될 고려사항

효과적인 클래스 설계 이전에 우리가 직면하게 될 고려사항이 무엇인지부터 파악하자!


Q. 새로 정의한 타입의 객체 생성 및 소멸은 어떻게 이루어져야 하는가?
A. 이 해답에 따라 클래스 생성자 및 소멸자의 설계가 바뀐다. 또한 메모리 할당 함수(operator new, operator new[], operator delete, operator delete[])를 직접 작성할 경우 이 함수들의 설계에도 영향을 준다.


Q. 객체 초기화는 객체 대입과 어떻게 달라야 하는가?
A. 생성자와 대입 연산자의 동작 및 둘의 차이점을 결정짓는 요소이다. 초기화와 대입을 헷갈리지 않는 것이 가장 중요하다. 각각에 해당되는 함수 호출이 아예 다르기 때문이다. 항목 4


Q. 새로운 타입으로 만든 객체가 pass by value로 전달되는 경우에 어떤 의미를 줄 것인가?
A. 어떤 타입에 대해 pass by value를 구현하는 쪽은 복사 생성자이다. 명심하자.


Q. 새로운 타입이 가질 수 있는 적법한 값에 대한 제약은 무엇으로 잡을 것인가?
A. 클래스의 데이터 멤버의 몇 가지 조합 값만은 반드시 유효해야 하는데, 이 조합을 클래스의 불변속성(invariant) 이라고 한다. 불변속성은 클래스 차원에서 지켜주어야 하기 때문에 각종 멤버 함수, 특히 생성자, 대입 연산자, 각종 setter 함수들이 불변속성의 영향을 받는다.
또한 불변속성은 함수가 발생시키는 예외에도 영향을 미치며, 예외 지정(exception specification)을 쓴다면 그 부분에도 영향을 미친다.

참고: 불변속성이란?


Q. 기존의 클래스 상속 계통망(inheritance graph)에 맞출 것인가?
A. 이미 존재하는 클래스로부터 상속 받는다면, 당연히 클래스 설계는 상속하는 클래스에 의해 제약을 받는다. 특히 멤버 함수가 가상인지 비가상인지 여부가 가장 큰 요인이다. 항목 34, 36
만약 설계할 클래스를 다른 클래스들이 상속받을 수 있게 하려면, 이에 따라 멤버 함수의 가상 함수 여부가 결정된다. (특히 가상 함수가 하나라도 있다면, 소멸자는 가상 함수여야 한다. 항목 7)


Q. 어떤 종류의 타입 변환을 허용할 것인가?
A. 우리가 만든 타입은 기존의 수많은 타입들과 함께 사용된다. 따라서 설계하고 있는 타입이 다른 타입으로 변환될 수단이 있어야 할 지 고려하여야 한다.

T1 타입 객체를 T2 타입 객체로 암시적으로 변환하고자 한다면, operator T1()와 같은 타입 변환 함수나 인자 한 개로 호출될 수 있는 non-explicit 생성자 T2(T1& t1)를 만들어야 한다.
만약 명시적 변환만 허용하고 싶다면, 명시적 변환 함수를 만들되 타입 변환 연산자나 non-explicit 생성자를 만들지 않아야 한다. 항목 15


Q. 어떤 연산자와 함수를 두어야 의미가 있을까?
A. 클래스 안에 선언할 함수가 이 질문의 답에 의해 결정된다. 어떤 것은 멤버 함수로 만들겠지만, 어떤 것은 그렇지 않을 것이다. 항목 23, 24, 26


Q. 표준 함수들 중 어떤 것을 허용하지 말 것인가?
A. 항목 6에서 알 수 있다. 허용하지 않을 표준 함수들은 private으로 선언하자.


Q. 새로운 타입의 멤버에 대한 접근 권한을 어느 쪽에 줄 것인가?
A. 이 질문은 어떤 클래스 멤버를 public, protected, private 중 어느 영역에 둘 것인지와 어떤 클래스 및 함수를 프렌드로 만들어야 할 지를 결정한다.
더하여 한 클래스를 다른 클래스에 중첩시켜도 되는지까지 결정한다.

참고: [C++] Inner Class (중첩 클래스, Nested Class)


Q. '선언되지 않은 인터페이스'로 무엇을 둘 것인가?
A. 만들 타입이 제공할 보장이 어떤 종류인가에 대한 질문이다. 보장할 수 있는 부분은 수행 성능 및 예외 안전성(항목 29), 자원 사용(잠금, 동적 메모리 등)이다. 이들에 대해 보장하겠다고 결정했다면 클래스 구현에 있어 제약으로 작용하게 된다.
(아직은 무슨 소린지 모르겠음.. 항목 29를 읽고나면 이해되겠지?)


Q. 새로 만드는 타입이 얼마나 일반적인가?
A. 실상은 타입 하나를 정의하는 것이 아닐지도 모른다. 지금 정의하는 타입이 종일 계열의 타입군 전체(family of types)일지도 모른다. 그렇다면 새로운 클래스가 아니라 새로운 클래스 템플릿을 정의해야 한다.

참고: 클래스 템플릿


Q. 정말로 꼭 필요한 타입인가?
A. 기존의 클래스에 기능 몇 개를 추가하고 싶어서 파생 클래스를 만드는 것이 옳은 선택일까? 차라리 간단하게 비멤버 함수나 템플릿은 몇 개 더 정의하는 것이 낫다.

0개의 댓글