protected 継承の使い方

Container Wrappers - Life like a clownSTL のコンテナを継承しよう(と見せかけよう)と,最後らへんは迷走か?と言うレベルで頑張っていましたが,以下にもう少し単純な方法が紹介されていました.

2.STLvectorをprotected継承してカスタマイズする
例えばこんな感じ。

#include <vector>

template <class T, int N, class Allocator = std::allocator<T> >
class Vector : protected std::vector<T, Allocator>
{
public:
    using std::vector<T, std::allocator<T> >::iterator;
    using std::vector<T>::get_allocator;
    using std::vector<T>::max_size;
    using std::vector<T>::size;
    using std::vector<T>::empty;
    ....
    
    explicit Vector(const Allocator& = Allocator()) : std::vector<T, Allocator>() {
        this->reserve(N);
    }
};

int main()
{
	Vector<int, 100> v;
	
	printf("capacity:%d, size:%d\n", v.capacity(), v.size());
}

・・・(中略)・・・
これをすることで、例えばpush_backの際にcapacityが変化したか、といったような事を監視できるし、(ただし、insertなどのメソッドでもcapacityは変化する)使わせたくないインターフェイスを公開しない、という事もできるし、

STLのvectorに文句を言わずに自分で頑張る方法 - 神様なんて信じない僕らのために

このように STL のコンテナを継承して使いたい場合は,対象とするコンテナクラスを protected 継承すれば良いようです.元のコンテナクラスの全てのメソッドをユーザに提供したいのような場合には少々面倒ですが,例えば,ユーザには begin()/end() くらいしか提供しないと言うような場合にはこの方法の方が良いなと思いました.

ところで,最初,なぜ protected 継承なのだろうと思った(と言うか,デストラクタがvirtualじゃないクラス、例えばvectorは継承しちゃだめなんだぞー! - 神様なんて信じない僕らのためにの意図が分からなかった)のですが,以下のコードを書いてみて納得しました.

#include <cstdio>

class Container {
public:
    ~Container() {
        printf("Container::~Container()\r\n");
    }
};

class Hoge : protected Container {
public:
};

int main() {
    Container* hoge = new Hoge();
    delete hoge;
    
    return 0;
}
$ g++ -Wall inherit.cpp
inherit.cpp: In function 'int main()':
inherit.cpp:15: error: 'Container' is an inaccessible base of 'Hoge'

protected 継承にしておく事によって,アップキャストをコンパイルエラーにしてしまう事ができるようです.これによって,基底クラスのポインタで delete しようとするユーザコードをコンパイルの段階で弾く事ができます.

広告を非表示にする