항목 31 : 파일 사이의 컴파일 의존성을 최대로 줄이자
객체 참조자 및 포인터로 충분한 경우에는 객체를 직접 쓰지 않습니다.
어떤 타입에 대한 참조자 및 포인터를 정의할 대는 그 타입의 선언부만 필요합니다. 반면, 어떤 타입의 객체를 정의할 때는 그 타입의 정의가 준비되어 있어야 합니다.
할 수 있으면 클래스 정의 대신 클래스 선언에 최대한 의존하도록 만듭니다.
어떤 클래스를 사용하는 함수를 선언할 때는 그 클래스의 정의를 가져오지 않아도 됩니다. 심지어 그 클래스 객체를 값으로 전달하거나 반환하더라도 클래스 정의가 필요없습니다.
선언부와 정의부에 대해 별도의 헤더 파일을 제공합니다.
"클래스를 둘로 쪼개자"라는 지침을 제대로 쓸 수 있도록 하려면 헤더 파일이 짝으로 있어야 합니다. 하나는 선언부를 위한 헤더 파일이고, 또 하나는 정의부를 위한 헤더 파일입니다. 이 두 파일은 관리도 짝 단위로 해야 합니다. 한쪽에서 어떤 선언이 바뀌면 다른 쪽도 똑같이 바꾸어야 한다는 것 입니다.
인터페이스 클래스를 구현하는 용도로 가장 많이 쓰이는 메커니즘이 두 가지 있습니다.
1. 인터페이스 클래스로부터 인터페이스 명세를 물려받게 만든 후에, 그 인터페이스에 들어 잇는 함수를 구현하는 것
2. 다중 상속을 사용하는 것
핸들 클래스와 인터페이스 클래스는 구현부로부터 인터페이스를 뚝 떼어 놓음으로써 파일들 사이의 컴파일 의존성을 완화시키는 효과를 가져다줍니다.
핸들 클래스부터 따져 보겠습니다.
핸들 클래스의 멤버 함수를 호출하면 구현부 객체의 데이터까지 가기 위해 포인터를 타야 합니다. 한 번 접근할 때마다 요구되는 간접화 연산이 한 단계 더 증가하는 것이죠.
둘째, 객체 하나씩을 저장하는데 필요한 메모리 크기에 구현부 포인터의 크기가 더해집니다.
마지막으로, 구현부 포인터가 동적 할당된 구현부 객체를 가리키도록 어디선가 그 구현부 포인터의 초기화가 일어나야 합니다.
결국 동적 메모리 할당에 따르는 연산 오버헤드는 물론이고, bad_alloc(메모리 고갈) 예외와 맞부딪힐 가능성까지 있습니다.
인터페이스 클래스의 경우에는 호출되는 함수가 전부 가상 함수라는 것이 약점입니다. 따라서 함수 호출이 일어날 때마다 가상 테이블 점프에 따르는 비용이 소모됩니다. 게다가, 인터페이스 클래스에서 파생된 객체는 죄다 가상 테이블 포인터를 지니고 있어야 합니다. 만약 가상 함수를 공급하는 쪽이 인터페이스 클래스밖에 없을 때는, 이 가상 테이블 포인터도 객체 하나를 저장하는 데 필요한 메모리 크기를 늘리는 요인이 됩니다.
마지막으로, 핸들 클래스와 인터페이스 클래스가 똑같이 갖고 있는 약점이 있습니다.
인라인 함수의 도움을 제대로 끌어내기 힘들다는 점입니다. 인라인이 되게 만들려면 함수 본문을 대개 헤더 파일에 두어야 합니다. 그렇지만 핸들 클래스와 인터페이스 클래스는 함수 본문과 같은 구현부를 사용자의 눈으로부터 차단하는 데 중점을 둔 설계입니다.
지금까지 말한 이런저런 부분에서 핸들 클래스와 인터페이스 클래스가 비용 소모를 가져온다고 해서 사용을 하지 않는것은 좋지 않습니다. 이런 기법들은 미래를 대비한다는 느낌으로 대하는게 좋습니다.
꼭 잊지 말아야 할 것!
1. 컴파일 의존성을 최소화하는 작업의 배경이 되는 가장 기본적인 아이디어는 '정의' 대신에 '선언'에 의존하게 만들자는 것입니다. 이 아이디어에 기반한 두 가지 접근 방법은 핸들 클래스와 인터페이스 클래스입니다.
2. 라이브러리 헤더는 그 자체로 모든 것을 갖추어야 하며 선언부만 갖고 있는 형태여야합니다. 이 규칙은 템플릿이 쓰이거나 쓰이지 않거나 동일하게 적용합시다.
'언어 > C++' 카테고리의 다른 글
[Effective C++] 상속된 이름을 숨기는 일은 피하자 (0) | 2020.02.02 |
---|---|
[Effective C++] public 상속 모형은 반드시 "is-a(...는 ...의 일종이다)"를 따르도록 만들자 (0) | 2020.02.01 |
[Effective C++] 인라인 함수는 미주알고주알 따져서 이해해 두자 (0) | 2020.01.30 |
[Effective C++] 예외 안전성이 확보되는 그날 위해 싸우고 또 싸우자! (0) | 2020.01.29 |
[Effective C++] 내부에서 사용하는 객체에 대한 '핸들'을 반환하는 코드는 되도록 피하자 (0) | 2020.01.28 |