その学習のために、実例を作ってみました。
FieldPlayer.hpp
#ifndef FIELDPLAYER_HPP_
#define FIELDPLAYER_HPP_
class FieldPlayer
{
public:
FieldPlayer();
void Draw();
int Update();
int model_handle() const;
private:
int model_handle_;
};
#endif // FIELDPLAYER_HPP_
#include "FieldPlayer.hpp"
#include
FieldPlayer::FieldPlayer()
: model_handle_(-1)
{
}
void FieldPlayer::Draw()
{
if (model_handle_ != -1)
{
MV1DrawModel(model_handle_);
}
}
int FieldPlayer::Update()
{
return 0;
}
int FieldPlayer::model_handle() const
{
return model_handle_;
}
これから、このクラスにプレイヤーの位置を管理する機能を追加します。
FieldPlayer.hpp
#ifndef FIELDPLAYER_HPP_
#define FIELDPLAYER_HPP_
#include
class FieldPlayer
{
public:
FieldPlayer();
void Draw();
int Update();
int model_handle() const;
private:
int model_handle_;
VECTOR pos_; // これを追加
};
#endif // FIELDPLAYER_HPP_
#include "FieldPlayer.hpp"
#include
FieldPlayer::FieldPlayer()
: model_handle_(-1), pos_() // これを追加
{
}
void FieldPlayer::Draw()
{
if (model_handle_ != -1)
{
MV1DrawModel(model_handle_);
}
}
int FieldPlayer::Update()
{
if (model_handle_ != -1) // これを追加
{
pos_.x += 0.1f;
MV1SetPosition(model_handle_, pos_);
}
return 0;
}
int FieldPlayer::model_handle() const
{
return model_handle_;
}
このこと自体は、C++をやっている人には至極当たり前のことでしょう。
FieldPlayer.hppに実装が露出しているというのも問題ですが、もっと実際的な問題としてはFieldPlayer.hppを#includeしているすべてのcppファイルをコンパイルし直さないといけないということです。
内部実装を変更しただけなのに、膨大な再コンパイル時間がかかるというのは、作業効率を低下させます。
そこで、pImplイディオムを用いて書き換えてみましょう。以下、pos_を追加する前のソースのpImpl版です。
FieldPlayer.hpp
#ifndef FIELDPLAYER_HPP_
#define FIELDPLAYER_HPP_
#include
class FieldPlayer
{
public:
FieldPlayer();
~FieldPlayer(); // pImplイディオムでstd::unique_ptrを使う場合、明示的にデストラクタを宣言するのが重要。
void Draw(); // 公開インターフェース
int Update(); // 公開インターフェース
int model_handle() const; // 公開インターフェース
private:
class Impl;
std::unique_ptr impl_; // 実装へのポインタを持つ
};
#endif // FIELDPLAYER_HPP_
#include "FieldPlayer.hpp"
#include
class FieldPlayer::Impl
{
public:
Impl();
void Draw();
int Update();
int model_handle() const;
private:
int model_handle_;
};
FieldPlayer::Impl::Impl()
: model_handle_(-1)
{
}
void FieldPlayer::Impl::Draw()
{
if (model_handle_ != -1)
{
MV1DrawModel(model_handle_);
}
}
int FieldPlayer::Impl::Update()
{
return 0;
}
int FieldPlayer::Impl::model_handle() const
{
return model_handle_;
}
FieldPlayer::FieldPlayer()
: impl_(new Impl())
{
}
FieldPlayer::~FieldPlayer()
{
}
void FieldPlayer::Draw()
{
impl_->Draw();
}
int FieldPlayer::Update()
{
return impl_->Update();
}
int FieldPlayer::model_handle() const
{
return impl_->model_handle();
}
FieldPlayer.cpp
#include "FieldPlayer.hpp"
#include
class FieldPlayer::Impl
{
public:
Impl();
void Draw();
int Update();
int model_handle() const;
private:
int model_handle_;
VECTOR pos_; // これを追加
};
FieldPlayer::Impl::Impl()
: model_handle_(-1), pos_() // これを追加
{
}
void FieldPlayer::Impl::Draw()
{
if (model_handle_ != -1)
{
MV1DrawModel(model_handle_);
}
}
int FieldPlayer::Impl::Update()
{
if (model_handle_ != -1) // これを追加
{
pos_.x += 0.1f;
MV1SetPosition(model_handle_, pos_);
}
return 0;
}
int FieldPlayer::Impl::model_handle() const
{
return model_handle_;
}
FieldPlayer::FieldPlayer()
: impl_(new Impl())
{
}
FieldPlayer::~FieldPlayer()
{
}
void FieldPlayer::Draw()
{
impl_->Draw();
}
int FieldPlayer::Update()
{
return impl_->Update();
}
int FieldPlayer::model_handle() const
{
return impl_->model_handle();
}
このように、pImplイディオムを用いると、実装を書き換えた場合にヘッダファイルを修正する頻度が劇的に減ります。
(もちろん、public関数を追加したりプロトタイプ宣言を変えたりするには、ヘッダファイルの変更と、それに伴う再コンパイルが必要ですが。)