본문 바로가기

Effective C++/4. 설계 및 선언

항목 19: 클래스 설계는 타입 설계와 똑같이 취급하자.

클래스 정의 => 사실 type 정의임

 

어떤 클래스를 설계할 때는 type 하나 만든다고 생각하고 최대한 열심히 만들자. 클래스를 설계할 여러 고민들이 생기는데..

 

  • 새로 정의한 타입의 객체 생성 소멸은 어떻게 이루어져야 하나?

부분이 어떻게 되느냐에 따라 생성자, 소멸자의 설계가 바뀐다. 아니라 메모리 할당 함수(operator new, operator new[], operator delete, operator delete[]) 직접 작성할 경우에는 이들 함수의 설계에도 영향을 미친다.

 

  • 객체 초기화는 객체 대입과 어떻게 달라야 하는가?

생성자 vs 대입 연산자의 차이를 결정짓는 요소이다. 초기화와 대입을 햇갈리지 않는 것이 가장 중요한데, 각각에 해당하는 함수 호출이 아예 다르기 때문이다.(항목 4 참조)

 

  • 새로운 타입으로 만든 객체가 값에 의해 전달되는 경우에 어떤 의미를 줄 것인가?

값에 의한 전달 => 복사 생성자에 의해 이루어짐. 전달할 객체가 매개 변수의 복사 생성자의 인자로 들어감

 

  • 새로운 타입이 가질 있는 적법한 값에 대한 제약은 무엇으로 잡을 것인가?

전부는 아니지만, 클래스의 데이터 맴버의 가지 조합 값만은 반드시 유효해야 한다.(클래스의 불변속성(invariant)) ->https://banaba.tistory.com/34

 

예를 들자면, 항목 18 Month 객체의 데이터 맴버 값이 항상 1~12 유지되는 것이 프로그래머의 의도라면, 그것이 객체가 생성되고 소멸될 까지 유지되어야하는 것이 Month 객체의 불변속성이 되는 것이다.

 

여튼, 불변속성에 따라 클래스 맴버 함수 안에서 해주어야 에러 점검 루틴이 결정된다. 특히 생성자, 대입연산자, 각종 "쓰기(setter)" 함수들은 불변속성에 많이 좌우된다. 아니라 맴버 함수가 발생시키는 예외에도 영향을 미치고, 혹시나 예외 지정(exception specification) 쓴다면 부분에서도 영향을 준다.

 

  • 기존의 클래스 상속 계통망(inheritance graph) 맞출 것인가?

만약 기존에 있던 클래스를 상속받는다고 하면, 우리의 설계는 클래스에 의해 제약을 받게 된다. 특히 맴버 함수가 가상인가 아닌가의 여부가 가장 요인이다.(항목 34, 36 참조) 그리고 만약 우리가 만들 클래스를 다른 클래스들이 상속할 있게 만들고 싶다면, 이에 따라 맴버 함수의 가상 함수 여부가 결정된다. 특히 소멸자의 경우가 그렇다.(항목 7 참조)

 

  • 어떤 종류의 타입 변환을 허용할 것인가?

우리가 만든 타입은 결국 기존의 수많은 타입들과 어울려야 한다. 그래서 우리의 타입과 다른 타입 사이에 변환 수단이 있어야 할지 결정해야 한다. 만약 T1 타입의 객체를 T2 타입의 객체로 암시적(Implicit) 변환되도록 만들고 싶다면 T1 클래스에 타입 변환 함수를 하나 넣어둬야 하고(operator T2()), 인자 하나로 호출될 수있는 비명시호출(non-explicit) 생성자를 T2 클래스에 넣어줘야 한다. (참고)

명시적(explicit) 타입 변환만 허용하고 싶으면, 해당 변환을 맡는 별도 이름의 함수(get()) 만들되, 타입 변환 연산자 혹은 (인자 하나로 호출될 있는) 비명시호출 생성자는 만들지 말아야 한다.

 

  • 어떤 연산자와 함수를 두어야 의미가 있을까?

클래스 안에 선언할 함수가 바로 여기서 결정된다. 어떤 함수는 맴버함수로 적절할 것이고, 어떤 함수는 아닐 것이다.

 

  • 표준 함수들 어떤 것을 허용하지 것인가?

private으로 선언해야 함수가 여기에 해당됨(항목 6 예시임)

 

  • 새로운 타입의 맴버에 대한 접근권한을 어느 쪽에 것인가?

어떤 클래스 맴버를 public, protected, private 영역에 것인가를 결정하는 요소이다. 또한 프랜드로 만들어야 클래스 함수를 정하는 것은 물론이고 클래스를 다른 클래스에 중첩시켜도 되는가에 대한 결정을 내리는 데도 질문이 우리한테 도움을 것이다.

 

  • "선언되지 않은 인터페이스" 무엇을 것인가?

우리가 만들 타입이 제공할 보장(?) 어떤 종류일까에 대한 질문이다. => 보장할 있는 부분은 수행 성능, 예외 안전성(항목 29 참고). 자원 사용(lock 동적 메모리 )이다. 이들에 대해 우리가 보장하겠다고 결정한 결과는 클래스 구현에 있어 제약으로 작용하게 된다.(?)

=> 어떤 것을 보장하느냐에 따라 어떤 인터페이스를 선언하고 선언하지 말지 결정된다는 뜻인듯

 

  • 새로 만드는 타입이 얼마나 일반적인가?

우리가 정의하는게 타입 하나(one type) 아니라 동일 계얼의 타입군(family of types)일지도 모른다. => 이러면 새로운 클래스가 아니라 새로운 클래스 템플릿 정의해야 한다.

 

  • 정말로  필요한 타입인가?

기존 클래스의 기능 몇개가 아쉬워서 파생 클래스를 만들고 있는 것이라면, 차라리 간단하게 비맴버 함수라던지 템플릿을 정의하는 편이 낫다.