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

トップページC++編

C++編で扱っている C++ は 2003年に登場した C++03 という、とても古いバージョンのものです。C++ はその後、C++11 -> C++14 -> C++17 -> C++20 -> C++23 と更新されています。
なかでも C++11 での更新は非常に大きなものであり、これから C++ の学習を始めるのなら、C++11 よりも古いバージョンを対象にするべきではありません。特に事情がないなら、新しい C++ を学んでください。 当サイトでは、C++14 をベースにした新C++編を作成中です。

問題①

問題① 第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 のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る