34項 コンパイルするファイル間の依存性はできるだけ減らそう

コンパイルするファイル間の依存性はできるだけ減らそう


多くincludeされているヘッダファイルの実装が変更されると、そのヘッダファイルを一度でもincludeしたファイルの再コンパイルを行うことになってしまいます。

本書の例を引用します。

#include <string>
#include "date.h"
#include "address.h"
#include "country.h"

class Person
{
public:
    Person(const string &name, const Date &birthday,
        const Address &addr, const Country &country);
    ~Person();
    ...

    string name() const ;
    string birthDate() const ;
    string address() const ;
    string nationality() const ;
private:
    string name_;
    Date birthDate_;
    Address address_;
    Country citizenship_;

Personクラスはname_,birthDate_,address_,citizenship_とクラスの実態を含んでいます。
もしこの4つのクラスの、いずれかの実装が変更された場合、Personクラスを使うすべてのファイルの再コンパイルが必要になります。



これを解決するためには、Personクラスの定義と、Personクラスに含まれるクラスの実装を切り離す必要があります。
本書では、以下のように切り離しています。

//先行宣言
class string;
class Date;
class Address;
class Country;

class PersonImpl;

class Person
{
public:
    Person(const string &name, const Date &birthday,
        const Address &addr, const Country &country);
    ~Person();
    ...

    string name() const ;
    string birthDate() const ;
    string address() const ;
    string nationality() const ;
pribate:
    PersonImpl *impl;

Personの実装は以下のようにします。

#include "Person.h"
#include "PersonImpl.h" 


Person::Person(const string &name, const Date &birthday,
        const Address &addr, const Country &country);
{
    impl = new PersonImpl(name, birthday, addr, country);
}
    
string Person::name() const
{
    return impl->name();
}

このように実装することで、string,Date,Address,Countryのいずれかのクラスの実装が変更されても、PersonImplの実装のみの再コンパイルにとどめられます。



本書には、切り離すときの3つの方針が挙げられています。
オブジェクトのリファレンスやポインタで間に合うときは、オブジェクトを使わない。
クラス宣言を使えるときは、クラス定義を使わない。
あなたのヘッダファイルは、コンパイルに必要なものを除いて、ほかのヘッダファイルを#includeしてはならない。