[Effective C++] Ch4. 설계 및 선언

y30n9ju1v·2021년 6월 28일
0

프로그래밍

목록 보기
6/18

Item 18. 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자

좋은 인터페이스는 제대로 쓰기에 쉬우며 엉터리로 쓰기에 어려움
인터페이스를 만들 때는 위 특성을 지닐 수 있도록 고민해야 함
인터페이스의 올바른 사용을 이끄는 방법으로는 인터페이스 사이의 일관성 잡아주기 그리고 기본 제공 타입과의 동작 호환성을 유지하 기임
사용자의 실수를 방지하는 방법으로는 새로운 타입 만들기, 타입에 대한 연산을 제한하기, 객체의 값에 대해 제약 걸기, 자원 관리 작업을 사용자 책임으로 놓지 않기가 있음

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

클래스를 설계할 때는 마치 언어 설계자가 그 언어의 기본제공 타입을 설계하면서 쏟아 붓는 것과 똑같은 정성과 보살핌이 필요함

  • 새로 정의한 타입의 객체 생성 및 소멸은 어떻게 이루어져야 하는가?
  • 객체 초기화는 객체 대입과 어떻게 달라야 하는가?
  • 새로운 타입으로 만든 객체가 값에 의해 전달되는 경우에 어떻 의미를 줄 것인가?
  • 새로운 타입이 가질 수 있는 적법한 값에 대한 제약은 무엇으로 잡을 것인가?
  • 기존의 클래스 상속 계통망(inheritance graph)에 맞출 것인가?
  • 어떤 종류의 타입 변환을 허용할 것인가?
  • 어떤 연산자와 함수를 두어야 의미가 있을까?
  • 표준 함수들 중 어떤 것을 허용하지 말 것인가?
  • 새로운 타입의 멤버에 대한 접근권한을 어느 쪽에 줄 것인가?
  • '선언되지 않은 인터페이스'로 무엇을 둘 것인가?
  • 새로 만드는 타입이 얼마나 일반적인가?
  • 정말로 꼭 필요한 타입인가?

Item 20. '값에 의한 전달'보다는 '상수객체 참조자에 의한 전달' 방식을 택하는 편이 대게 낫다

기본적으로 C++는 함수로부터 객체를 전달받거나 함수에 객체를 전달할 때 '값에 의한 전달(call-by-value)' 방식을 사용함
'값에 의한 전달'보다는 '상수 객체 참조자에 의한 전달(call-by-reference)'을 선호해야 하는데 대체적으로 효율적일뿐만 아니라 복사손실 문제까지 막아 주기때문임
하지만 기본제공 타입 및 STL 반복자, 그리고 함수 객체 타입에는 '값에 의한 전달'이 더 적절함
왜냐하면 반복자와 함수 객체를 구현할 때는 반드시 (1) 복사 효율을 높일 것과 (2) 복사손실 문제에 노출되지 않도록 만드는 것을 필수로 했기 때문임

Item 21. 함수에서 객체를 반환해야 할 경우에 참조자를 반환하려고 들지 말자

  1. 스택변수를 참조자로 반환하면 함수가 끝난뒤 그 변수는 사라지기 때문에 위험함
  2. 힙변수를 참조자로 반환하면 그 변수를 제거해 줘야 하는 문제가 발생함
  3. 정적변수를 이용할 경우 멀티 쓰레드 문제가 발생할 수 있음
  4. 싱글 쓰레드라도 같은 정적변수를 참조하는 경우 문제가 발생

Item 22. 데이터 멤버가 선언될 곳은 private 영역임을 명심하자

만일 어떤 데이터 멤버를 public으로 내놨다면 모두가 이 멤버에 대해 읽기 및 쓰기 접근권한을 갖게 되지만 이 값을 읽고 쓰는 함수가 있으면 접근 불가, 읽기 전용, 읽기 쓰기 접근을 우리가 직접 구현할 수 있음
데이터 멤버를 함수 인터페이스 뒤에 감추게 되면 구현상의 융통성을 누릴 수 있음 예를 들어 데이터 멤버를 읽거나 쓸 때 다른 객체에 알림 메시지를 보낸다던지, 클래스의 불변 속성 및 사전 조건, 사후 조건을 검증한다든지, 스레딩 환경에서 동기화를 건다는지 등
캡슐화 특성이 높아져 현재 구현을 나중에 바꾸기로 결정할 수 있는 권한이 예약됨
protected는 public보다 더 많이 보호받고 있는 것은 절대로 아님

Item 23. 멤버 함수보다는 비멤머 비프렌드 함수와 더 가까워지자

멤버 함수들을 여러 개 호출하는 멤버 함수를 추가하는 것보다는 비멤버 비프렌드 함수를 자주 쓰도록 해야 함
이는 캡슐화 정도가 높아지고(비멤버 비프렌드 함수는 private 멤버 변수를 접근하는 함수 개수를 늘리지 않음) 패키징 유연성도 커지며, 기능적인 확장성도 늘어남

Item 24. 타입 변환이 모든 매개변수에 대해 적용되어야 한다면 비멤버 함수를 선언하자

어떤 클래스와 연관 관계를 맺어 놓고는 싶은데 멤버 함수면 안 되는(모든 인자에 대해 타입 변환이 필요하다든가 하는 이유로) 함수에 대해 이런 것들을 프렌드로 만들어 해결하면 안 됨
어떤 함수에 들어가는 모든 매게 변수(this 포인터가 가리키는 객체도 포함해서)에 대해 타입 변환을 해 줄 필요가 있다면 그 함수는 비멤버이어야 함

Item 25. 예외를 던지지 않는 swap에 대한 지원도 생각해 보자

  1. 표준에서 제공하는 swap이 여러분의 클래스 및 클래스 템플릿에 대해 납득할 만할 효율을 보이면 그냥 아무것도 하지 말자
  2. 표준 swap의 효율이 기대한 만큼 충분하지 않다면 다음과 같이 시도해 볼 것
    a. 여러분의 타입으로 만들어진 두 객체의 값을 빛나게 빨리 맞바꾸는 함수를 swap이라는 이름으로 만들고 이것을 public 멤버 함수로 둠
    b. 여러분의 클래스 혹은 템플릿이 들어 있는 네임스페이스와 같은 네임스페이스에 비멤버 swap을 만들어 넣고 a번에서 만든 swap 멤버 함수를 이 비멤버 함수가 호출할 수 있도록 만듦
    c. 새로운 클래스를 만들고 있다면 그 클래스에 대한 std::swap의 특수화 버전을 준비해 두고 이 특수화 버전에서도 swap 멤버 함수를 호출하도록 만듦
  3. 사용자 입장에서 swap을 호출할 때 swap을 호출하는 함수가 std::swap을 볼 수 있도록 using 선언을 반드시 포함시킵니다. 그다음에 swap을 호출하되 네임스페이스 한정자를 붙이지 않도록 함
  4. 멤버 버전의 swap은 절대로 예외를 던지지 않도록 만들어야 함

0개의 댓글