Skip to content

Latest commit

 

History

History
75 lines (68 loc) · 4.34 KB

Item31.md

File metadata and controls

75 lines (68 loc) · 4.34 KB

항목 31. 파일 사이의 컴파일 의존성을 최대로 줄이자

컴파일 의존성

  • C++는 인터페이스와 구현을 깔끔하게 분리하는 일에 별로 일가견이 없다.
#include "date.h"
#include "address.h"
#include <string>

class Person{
public:
    std::string address() const;
    std::string birthDate() const;
    std::string name() const;
private:
    Date theBirthDate;   // 구현 세부사항
    Address theAddress;  // 구현 세부사항
    std::string theName; // 구현 세부사항
}
  • #include문은 Person을 정의한 파일과 헤더 파일들 사이에 컴파일 의존성을 엮어버린다.
  • 헤더 파일 중 하나라도 바뀌거나 이들과 엮여 있는 헤더파일이 바뀌기만 해도 Person 클래스를 정의한 파일은 다시 컴파일 된다.
  • 컴파일러는 정의문을 만나면 객체를 담을 공간을 할당한다.
int x;
Person p{params}; // 1
Person *p; // 2
  1. Person 객체 하나의 크기를 알려면 정의부를 봐야 한다.
  2. Person 객체의 포인터를 담을 공간만 할당하면 된다.
    • Java의 경우 해당 방식으로 이루어져 있다.
    • C++도 포인터 뒤에 실제 객체 구현부를 숨길 수 있다.

정의부에 대한 의존성을 선언부에 대한 의존성으로 바꾸자

객체 참조자 및 포인터로 충분한 경우에는 객체를 직접 쓰지 않는다.

  • 어떤 타입에 대한 참조자 및 포인터를 정의할 때는 그 타입의 선언부만 필요하다.
  • 어떤 타입의 객체를 정의할 때는 그 타입의 정의가 준비되어 있어야 한다.

클래스 정의 대신 클래스 선언에 최대한 의존하도록 만든다.

  • 어떤 클래스를 사용하는 함수를 선언할 때는 그 클래스의 정의를 가져오지 않아도 된다.
  • 클래스 객체를 값으로 전달하거나 반환하더라도 클래스 정의가 필요없다.
class Date; // 클래스 선언

Date today();
void clearAppointments(Date d); // Date 클래스의 정의를 가져오지 않아도 된다.

선언부와 정의부에 대해 별도의 헤더 파일을 제공한다.

  • 선언부를 위한 헤더 파일과 정의부를 위한 헤더파일로 나눈다.
  • 라이브러리 사용자 쪽에서는 전방 선언 대신에 선언부 헤더 파일을 #include한다.
  • 라이브러리 제작자 쪽에서는 헤더 파일 두 개를 짝지어 제공해야 한다.
#include "datefwd.h" // Date 클래스를 선언하고 있는(정의하진 않는) 헤더 파일

Date today();
void clearAppointments(Date d);
  • 핸들 클래스인터페이스 클래스를 통해 구현부로부터 인터페이스를 떼어 놓아 파일들 사이의 컴파일 의존성을 완화시킬 수 있다.

핸들 클래스

  • pimpl 관용구를 사용하는 클래스를 핸들 클래스라고 한다.
  • 핸들 클래스의 멤버 함수를 호출하면 구현부 객체의 데이터까지 가기 위해 포인터를 타야 한다.
    • 한번 접근할 떄마다 요구되는 간접화 연산이 한 단계 더 증가한다.
  • 객체 하나씩을 저장하는데 필요한 메모리 크기에 구현부 포인터의 크기가 더해지는것도 필수이다.
  • 구현부 포인터가 동적 할당된 구현부 객체를 가리키도록 구현부 포인터의 초기화가 일어나야 한다.
  • 인라인 함수의 도움을 제대로 끌어내기 힘들다.

인터페이스 클래스