C++編【言語解説】 第28章 継承と合成 解答ページ

先頭へ戻る

このエントリーをはてなブックマークに追加

問題①

問題① 第26章の練習問題で、 標準ライブラリの bitset(【標準ライブラリ】第13章)に、 すべてのビットが 1 になっているかどうかを判定するメンバ関数を、公開継承を使って追加するという題材を扱いました。 同様のことを、普通の合成、非公開継承、限定公開継承のそれぞれを使って行って下さい。


まず、普通の合成を使った手法です。

#include <iostream>
#include <bitset>

template <std::size_t BITS>
class MyBitset {
public:
	inline bool all() const
	{
		return mBitset.size() == mBitset.count();
	}

	inline void set()
	{
		mBitset.set();
	}

	inline void set(std::size_t pos, bool val = true)
	{
		mBitset.set(pos, val);
	}

private:
	std::bitset<BITS>  mBitset;
};

int main()
{
	MyBitset<10> bset;

	bset.set();
	std::cout << std::boolalpha << bset.all() << std::endl;

	bset.set(0, false);
	std::cout << std::boolalpha << bset.all() << std::endl;
}

目的のメンバ関数を実装すること自体はとても簡単ですが、大きな問題があります。 それは、std::bitset が持つ「公開」メンバを、MyBitsetクラスの外部からは使用できないことです。 外部から呼び出せるようにするには、MyBitset側で同等のメンバを用意して、bitset のメンバへ転送する必要があります。 上のサンプルプログラムでは、setメンバ関数だけを用意しましたが、bitset のすべてのメンバに対して同じことをするのは、 非常に面倒ですし、厳密に同じ関数を宣言するのも意外と大変です。

普通の合成での面倒な部分をある程度回避できるのが、非公開継承を使った実装です。

#include <iostream>
#include <bitset>

template <std::size_t BITS>
class MyBitset : private std::bitset<BITS> {
public:
	using std::bitset<BITS>::set;

	inline bool all() const
	{
		return size() == count();
	}
};

int main()
{
	MyBitset<10> bset;

	bset.set();
	std::cout << std::boolalpha << bset.all() << std::endl;

	bset.set(0, false);
	std::cout << std::boolalpha << bset.all() << std::endl;
}

継承の場合は、using を使って、その名前をスコープに取り込むことができます。 上のサンプルプログラムで言えば、bitset のスコープに存在していた set が、 今や MyBitset のスコープにあるということです。 また、using は「公開」の場所に書いているので、set はきちんと「公開」されます。
これでも、bitset のすべてのメンバを1つ1つ using していく必要はありますが、 転送関数を作成していくよりは、ずっとましです。

最後に、限定公開継承を使った実装です。

#include <iostream>
#include <bitset>

template <std::size_t BITS>
class MyBitset : protected std::bitset<BITS> {
public:
	using std::bitset<BITS>::set;

	inline bool all() const
	{
		return size() == count();
	}
};

int main()
{
	MyBitset<10> bset;

	bset.set();
	std::cout << std::boolalpha << bset.all() << std::endl;

	bset.set(0, false);
	std::cout << std::boolalpha << bset.all() << std::endl;
}

これは、非公開継承の場合とまったく同じです。 本編で触れたように、限定公開継承のポイントは、更なる派生クラスから、 基底クラスの「公開」「限定公開」メンバをアクセスできることです。 従って、MyBitsetクラスから派生させるつもりが無いのなら、限定公開継承を使う理由も無く、非公開継承を使えば良いです。
一方、MyBitsetクラスから派生させるのなら、派生クラスから bitset のメンバをアクセスできることは利点になります。

template <std::size_t BITS>
class MyBitset2 : public MyBitset<BITS> {
public:
	// 先頭と末尾のビットが同じ値か
	inline bool is_equal_head_and_tail() const
	{
		return test(0) == test(size() - 1);
	}
};

is_equal_head_and_tailメンバ関数内で、bitset の testメンバ関数や sizeメンバ関数を呼び出しています。 このように、更なる派生クラスのメンバ関数内からは、限定公開継承の基底になっている std::bitset の「公開」「非公開」メンバを 呼び出すことができます。


参考リンク

更新履歴

'2016/4/2 新規作成。



第28章のメインページへ

C++編のトップページへ

Programming Place Plus のトップページへ