[C++] 実装の隠蔽:クラスを継承する場合

2011 年 9 月 2 日

Pimplイディオムの場合もインターフェイスの場合も、基本は公開用クラスも実装用クラスもそれぞれ継承すればいいだけ。

Pimplイディオム

継承元クラス

//--- Sample.h
class Impl; // 実装用クラスの前方宣言

class Sample {
public:
    Sample(const int num);
    Sample();
    Sample(const Sample&);
    ~Sample();
 
    Sample& operator=(const Sample&);
 
    int getNum() const;
 
private:
    SampleImpl* impl;
};

//---Impl.h
class Impl {
private:
    int num;
 
public:
    Impl(const int num) : num(num) { };
    Impl() : num(1) { }
 
    virtual int getNum() const { return num; }
};
 
//--- Sample.cpp
#include "Sample.h"
#include "Impl.h"

Sample::Sample(const int num) : impl( new Impl(num) ) {
 
}
 
Sample::Sample() : impl(new Impl) {
 
}
 
Sample::Sample(const Sample& base) {
    // Implの新しいインスタンスをコピーコンストラクタを使って生成
    Impl* newObj = new Impl(*base.impl);
    impl = newObj;
}
 
Sample::~Sample() {
    delete impl;
}
 
Sample& Sample::operator=(const Sample& base) {
    // コピーコンストラクタの場合と同様、implを新しく生成
    Impl* newObj = new Impl(*base.impl);
    delete impl;
    impl = newObj;
 
    return *this;
}
 
int Sample::getNum() const {
    return impl->getNum(); // 委譲
}

継承先クラス

//---SampleEx.h
#include "Sample.h"

class ImplEx; // SampleEx用の実装用クラスの前方宣言

class SampleEx : public Sample {
private:
	ImplEx* implEx;

public:
	int getNum() const { //... } // オーバーライド
};

//--- ImplEx.h
#include "Impl.h"

class ImplEx : public Impl {
	//...
};

//--- SampleEx.cpp
#include "SampleEx.h"
#include "ImplEx.h"

//...

インターフェイス

継承元クラス

//--- ISample.h
class ISample
{
public:
    virtual ~ISample(void){}
 
    virtual int getNum() const = 0;
 
    static ISample* createInstance();
    static ISample* clone(ISample* base);
};

//--- Sample.h
#include "ISample.h"
 
class Sample : public ISample { // インターフェイスの実装
private:
    int num;
 
public:
    Sample(const int num) : num(1) { }
    virtual ~Sample() { }
 
    int getNum() const { return num; }
}

//--- ISample.cpp
#include "ISample.h"
#include "Sample.h"
 
ISample* ISample::createInstance() { return new Sample; }
ISample* ISample::clone(ISample* base) { return new Sample(*base); }

継承先クラス

//--- ISampleEx.h
class ISampleEx : public ISample {
	//...
};

//--- SampleEx.h
class SampleEx : virtual public Sample, virtual public ISampleEx {
	//...
};

//--- ISampleEx.cpp
#include "ISampleEx.h"
#include "SampleEx.h"
//...

ごちゃごちゃしているようにも感じるが、どちらも公開用クラスと実装用クラスはそれぞれ別物だと考えればいい。

よって、あとで実装用クラスを公開することも可能。特にPimplならば、実装用クラスの名前を公開用クラスに変えるだけで済み、クラスを利用する側では記述の変更は必要ない。