C++ 継承のprivate / protected / publicの切り替え

アクセス権限の意味

アクセス指定子をつけると設定できる

private

該当する関数または変数には,それらが実装されたクラス(のメソッド)のみアクセスできる

protected

該当する関数または変数には,それらが実装されたクラス(のメソッド)とそのクラスから派生したクラス(のメソッド)のみアクセスできる

public

制限はない,オブジェクトを無から生成して渡すためにpublicの関数を経由する必要がある

アクセス権限の規制

継承時にアクセス指定子をつけると継承元の関数または変数のアクセス権限にリミットがかかる 隠蔽する方向にのみ動作する

private

publicprotectedprivateになる

class A{
    private:
        int x; // can't acccess from B
    protected:
        int y;
    public:
        int z;
}

class B:private A{
    // y -> private
    // z -> private
}

protected

publicprotectedになる

class A{
    private:
        int x; // can't acccess from B
    protected:
        int y;
    public:
        int z;
}

class B:protected A{
    // y -> protected
    // z -> protected
}

public

制限はない

class A{
    private:
        int x; // can't acccess from B
    protected:
        int y;
    public:
        int z;
}

class B:private A{
    // y -> protected
    // z -> public
}

アクセス権限の変更

using::でスコープをつけながらエイリアスを作成する際,アクセス指定子を先頭につければ個別に上書きできる

昇格

protectedからpublicにする

class A{
    protected:
        int x;
}

class B:public A{
    public using A::x;
    // reachable A.x from public, protected, private
}

使い道

特に思いつかないし,こんなものが必要になるような場面に遭遇したら,ひとまず設計を修正した方が良い気がしています

一括で昇格させる方法

これがどうやら存在しないらしい.やるならば全てに一対一でpublic using Class::nameを叩く必要があるらしい

降格

publicからprotectedにする

class A{
    public:
        int x;
}

class B:public A{
    protected using A::x;
    // reachable A.x from protected, private
}

publicからprivateにする

class A{
    public:
        int x;
}

class B:public A{
    private using A::x;
    // reachable A.x from private
}

protectedからprivateにする

class A{
    protected:
        int x;
}

class B:public A{
    private using A::x;
    // reachable A.x from private
}

使い道

部分的にprotectedprivateにしたいです,特に組込実装ではコード領域すらも節約する必要があるかと思います

一括で降格させる方法

つまり継承時にアクセス権限の規制をやれということで

多重継承

名前衝突や菱形問題の原因になるのでやめたい もしくはせめて仮想継承をちゃんと実装したい

名前衝突

衝突しなかったらスコープ無しで派生クラスから呼べる 衝突したらスコープ派生元クラスを解決する必要がある もし衝突してもエイリアスの作成は1個までならセーフ

class A{protected:int x;};
class B{protected:int x;};
class C:public A, public B{
    public: using A::x; // -> OK
    // public: using B::x; -> error
};

int main(void){
    C c;
    c.x = 0;
}

言い換えれば権限の変更は1個しかできないということ むしろ今までのケースが「衝突しなかったらスコープ無しで派生クラスから呼べる」のを悪用して同名の再定義をしている

菱形問題

これらの例はABにそれぞれ独立にxがあった上でCに合体させたせいで詰んでいるが,xを持つAからBCに派生させた上でDに合体させるような場合,(BCが悪さをしない限り)仮想継承でxは何とかなるかも

仮想継承

#include <iostream>
using namespace std;

class A{
    protected:
      int x;
};

class B: virtual public A{};

class C: virtual public A{};

class D: public B, public C{
    private: using C::x;
    protected: using B::x;
    public: using A::x;
    // both lines are equivalent in operation (why?)
    // `public` is needed to be specified in the end
};


int main(void){
    D d;
    d.x = 1;
    //d.A::x = 1; -> error if A::x is placed in protected
    cout << d.x << endl;

};

A::xの実体をB::xC::xD::xも指すが,アクセス権限はA::xB::xC::xD::xでそれぞれ設定され,かつusingエイリアスを作成するとその都度に設定が上書きされる(これでも実体は1つなので問題ない) 最終的にDpublic: usingするとD.xを介して実体を操作できる