C++におけるpublic
、private
、およびprotected
の継承の違いは何ですか?私がSOで見つけたすべての質問は特定のケースを扱います。
その質問に答えるために、私は自分の言葉で最初にメンバーのアクセサについて説明したいと思います。あなたがすでにこれを知っているならば、見出し "next:"へスキップしてください。
私が知っている3つのアクセサがあります:public
、protected
、private
。
みましょう:
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
Base
を知っていることはすべてBase
がpublicMember
を含んでいることも知っています。Base
にprotectedMember
が含まれていることを知っているのは、子供たち(およびその子供たち)だけです。Base
以外の誰もがprivateMember
を知っていません。「知っている」とは、「その存在を認め、アクセスできる」という意味です。
同じことが公的、私的および保護された継承でも起こります。 Base
クラスとChild
から継承するクラスBase
を考えてみましょう。
public
の場合、Base
とChild
を認識しているすべてのものは、Child
がBase
から継承していることも認識しています。protected
である場合、Child
とその子だけが、それらがBase
から継承することを認識しています。private
の場合、Child
以外の誰も継承を認識していません。class A
{
public:
int x;
protected:
int y;
private:
int z;
};
class B : public A
{
// x is public
// y is protected
// z is not accessible from B
};
class C : protected A
{
// x is protected
// y is protected
// z is not accessible from C
};
class D : private A // 'private' is default for classes
{
// x is private
// y is private
// z is not accessible from D
};
重要な注意:クラスB、C、Dはすべて変数x、y、zを含んでいます。ただアクセスの問題です。
保護された継承と私的な継承の使い方については here を読むことができます。
継承の可視性を制限すると、あるクラスが別のクラスを継承していることをコードが認識できなくなります。派生から基底への暗黙の変換も機能せず、基底から派生へのstatic_cast
も機能しません。
クラスのメンバー/友達だけがプライベート継承を見ることができ、メンバー/友達と派生クラスだけが保護された継承を見ることができます。
public継承
IS-Aの継承ボタンはウィンドウであり、ウィンドウが必要な場所であればどこにでもボタンを渡すことができます。
class button : public window { };
protectedの継承
条件付きで保護されています。めったに役に立ちません。空のクラスから派生し、空の基本クラスの最適化を使用してメモリを節約するためにboost::compressed_pair
で使用されます(以下の例では、その時点にあるためにテンプレートを使用しません)。
struct empty_pair_impl : protected empty_class_1
{ non_empty_class_2 second; };
struct pair : private empty_pair_impl {
non_empty_class_2 &second() {
return this->second;
}
empty_class_1 &first() {
return *this; // notice we return *this!
}
};
private継承
条件付きで実装。基本クラスの使い方は派生クラスを実装するためだけのものです。特性がある場合やサイズが重要な場合に便利です(関数のみを含む空の特性は空の基本クラス最適化を利用します)。多くの場合、 包含 がより良い解決策です。文字列のサイズは重要なので、ここでよく見られる使い方です
template<typename StorageModel>
struct string : private StorageModel {
public:
void realloc() {
// uses inherited function
StorageModel::realloc();
}
};
public member
集計
class pair {
public:
First first;
Second second;
};
アクセサ
class window {
public:
int getWidth() const;
};
保護メンバー
派生クラスへの拡張アクセスを提供する
class stack {
protected:
vector<element> c;
};
class window {
protected:
void registerClass(window_descriptor w);
};
privateメンバー
実装の詳細を守る
class window {
private:
int width;
};
Cスタイルのキャストでは、派生クラスを保護されたまたはプライベートの基本クラスに、定義された安全な方法でキャストしたり、反対方向にキャストしたりすることができます。コードを実装の詳細に依存させる可能性があるため、これは絶対に避けてください。ただし、必要に応じて、この手法を利用できます。
基本クラスのパブリックメンバーが派生クラスからどのように公開されるかと関係があります。
Litbが指摘しているように、パブリック継承は、ほとんどのプログラミング言語で見られる伝統的な継承です。つまり、それは "IS-A"関係をモデル化したものです。プライベート継承は、C++に特有のもので、「実装されている」という関係です。つまり、派生クラスのパブリックインタフェースを use にしたいのですが、派生クラスのユーザにそのインタフェースへのアクセスを許可したくない場合があります。この場合、基本クラスを集約する必要がある、つまり基本クラスをプライベート基本クラスとして持つのではなく、基本クラスの機能を再利用するためにderivedのメンバーを作成する必要があると多くの人が主張します。
これら3つのキーワードは、可視性継承モデルを指定するためにまったく異なるコンテキストでも使用されます。
このテーブルは、サブクラスが完全に定義されたときにコンポーネントへのアクセス結果を提示するコンポーネント宣言と継承モデルの可能な組み合わせすべてを集めたものです。
上の表は次のように解釈されます(最初の行を見てください)。
コンポーネントが宣言済み as publicで、そのクラスが継承 as publicの場合結果のアクセスpublicです。
例:
class Super {
public: int p;
private: int q;
protected: int r;
};
class Sub : private Super {};
class Subsub : public Sub {};
クラスSubsub内の変数p
、q
、r
に対するアクセス結果は、noneです。
もう一つの例:
class Super {
private: int x;
protected: int y;
public: int z;
};
class Sub : protected Super {};
クラスSub内の変数y
、z
に対するアクセス結果はprotectedであり、変数x
に対するアクセス結果はnoneです。
より詳細な例:
class Super {
private:
int storage;
public:
void put(int val) { storage = val; }
int get(void) { return storage; }
};
int main(void) {
Super object;
object.put(100);
object.put(object.get());
cout << object.get() << endl;
return 0;
}
サブクラスを定義しましょう。
class Sub : Super { };
int main(void) {
Sub object;
object.put(100);
object.put(object.get());
cout << object.get() << endl;
return 0;
}
Subという名前の定義済みクラス(Super
という名前のクラスのサブクラス)、またはそのSub
クラスは、Super
クラスから派生しています。 Sub
クラスは新しい変数も新しい関数も導入しません。 Sub
クラスのどのオブジェクトも、実際にはSuper
クラスのオブジェクトのコピーであるSuper
クラスの後にあるすべての特性を継承するという意味ですか?
いいえ。そうではありません。
次のコードをコンパイルすると、put
メソッドとget
メソッドにアクセスできないというコンパイルエラーが発生するだけです。どうして?
可視性指定子を省略すると、コンパイラはいわゆるprivate inheritanceを適用しようとしていると見なします。つまり、すべてのpublicスーパークラスコンポーネントがprivate accessになり、プライベートスーパークラスコンポーネントにはまったくアクセスできなくなります。したがって、サブクラス内で後者を使用することは許可されていません。
以前に使用したアクセスポリシーを保持したいことをコンパイラに通知する必要があります。
class Sub : public Super { };
誤解しないでください :それは、スーパークラスのプライベートコンポーネント(ストレージ変数のような)がやや不思議な方法でパブリックコンポーネントに変わることを意味するのではありません。 Privateコンポーネントはprivateのままになり、publicはpublicのままになります。
Sub
クラスのオブジェクトは、Super
クラスから作成された古い兄弟と「ほぼ」同じことをする可能性があります。 "ほぼ" サブクラスであるという事実は、クラスがスーパークラスのプライベートコンポーネントへのアクセスを失ったを意味するためです。ストレージ変数を直接操作できるSub
クラスのメンバー関数を書くことはできません。
これは非常に深刻な制限です。回避策はありますか?
はい。
3番目のアクセスレベルはprotectedです。キーワードprotectedは、それがマークされたコンポーネントが{いずれかのサブクラスによって使用される場合はパブリックなもののように振る舞い、その他の国々にとってはプライベートなもののように見えるを意味します。 - これは公的に継承されたクラスにのみ当てはまります(この例のSuperクラスのように) -
class Super {
protected:
int storage;
public:
void put(int val) { storage = val; }
int get(void) { return storage; }
};
class Sub : public Super {
public:
void print(void) {cout << "storage = " << storage;}
};
int main(void) {
Sub object;
object.put(100);
object.put(object.get() + 1);
object.print();
return 0;
}
サンプルコードに見られるように、私たちはSub
クラスへの新しい機能を持っています、そしてそれは1つの重要なことをします:それはSuperクラスからストレージ変数にアクセスします。
変数が非公開として宣言されている場合は不可能です。メインの関数スコープでは、とにかく変数は隠されたままなので、次のように書くと
object.storage = 0;
コンパイラはそれがerror: 'int Super::storage' is protected
であることを通知します。
最後に、最後のプログラムは以下の出力を生成します。
storage = 101
Member in base class : Private Protected Public
継承タイプ : - として継承されたオブジェクト :
Private : Inaccessible Private Private
Protected : Inaccessible Protected Protected
Public : Inaccessible Protected Public
1)公の継承 :
a。基本クラスの非公開メンバーは、派生クラスではアクセスできません。
b。基本クラスの保護されたメンバーは、派生クラスでも保護されたままです。
c。 Baseクラスの公開メンバーはDerivedクラスで公開されたままです。
したがって、他のクラスは、派生クラスオブジェクトを通じてBaseクラスのパブリックメンバーを使用できます。
2)保護された継承 :
a。基本クラスの非公開メンバーは、派生クラスではアクセスできません。
b。基本クラスの保護されたメンバーは、派生クラスでも保護されたままです。
c。 Baseクラスのパブリックメンバーも、Derivedクラスの保護されたメンバーになります。
したがって、他のクラスは、派生クラスオブジェクトを介してBaseクラスのパブリックメンバーを使用することはできません。しかし、それらはDerivedのサブクラスに利用可能です。
3)私的継承 :
a。基本クラスの非公開メンバーは、派生クラスではアクセスできません。
b。 Baseクラスの保護されたパブリックメンバーは、Derivedクラスのプライベートメンバーになります。
したがって、Baseクラスのメンバーは、派生クラス内ではプライベートなので、派生クラスオブジェクトを介して他のクラスからアクセスすることはできません。そのため、派生クラスのサブクラスでもアクセスできません。
パブリック継承は、IS-A関係をモデル化します。あり
class B {};
class D : public B {};
すべてのD
は B
です。
プライベート継承は、IS-IMPLEMENTED-USING関係(またはそれが呼ばれるものは何でも)をモデル化します。あり
class B {};
class D : private B {};
a D
は not a B
ですが、すべてのD
はその実装でそのB
を使用します。プライベート継承は、代わりに包含を使用することによって常に排除することができます。
class B {};
class D {
private:
B b_;
};
このD
もB
を使って、この場合はb_
を使って実装することができます。包含は継承より型間の密接な結びつきが少ないので、一般的には優先されるべきです。プライベート継承の代わりに包含を使用することはプライベート継承ほど便利ではないことがあります。多くの場合、それは怠惰であるための怠け者の言い訳です。
誰がどんなprotected
継承モデルを知っているとは思わない。少なくとも私はまだ説得力のある説明を見たことがない。
あなたが他のクラスから公に継承するならば、誰もがあなたが継承していることを知っています、そしてあなたはベースクラスポインタを通して誰でも多態的に使用することができます。
保護的に継承した場合、あなたの子供クラスだけがあなたを多態的に使うことができます。
あなたが個人的に継承した場合、あなただけが親クラスのメソッドを実行することができます。
これは基本的に、他のクラスがあなたの親クラスとのあなたの関係について持っている知識を象徴しています。
保護されたデータメンバーは、あなたのクラスから継承したどのクラスからもアクセスできます。ただし、個人データメンバーはできません。次のようになっているとしましょう。
class MyClass {
private:
int myPrivateMember; // lol
protected:
int myProtectedMember;
};
あなたのエクステンションの中からこのクラスまで、this.myPrivateMember
の参照は機能しません。しかし、this.myProtectedMember
はそうなります。値はまだカプセル化されているので、myObj
と呼ばれるこのクラスのインスタンス化があると、myObj.myProtectedMember
は機能しません。そのため、プライベートデータメンバと機能的には同じです。
Accessors | Base Class | Derived Class | World
—————————————+————————————+———————————————+———————
public | y | y | y
—————————————+————————————+———————————————+———————
protected | y | y | n
—————————————+————————————+———————————————+———————
private | | |
or | y | n | n
no accessor | | |
y: accessible
n: not accessible
に基づいて これ Javaの例...私は千語の価値がある小さなテーブルだと思います:)
概要:
継承時には、(一部の言語では)データメンバーの保護タイプを特定の方向に変更できます。から保護されてから。
基本クラスの非公開メンバーには、その基本クラスのメンバーからのみアクセスできます。
基本クラスのパブリックメンバーは、その基本クラスのメンバー、その派生クラスのメンバー、および基本クラスと派生クラスの外部にあるメンバーからアクセスできます。
基本クラスの保護されたメンバーは、基本クラスのメンバーとその派生クラスのメンバーからアクセスできます。
プライベート :base
protected :base + derived
public :base + derived +他のメンバー
私は簡単な答えを見つけたので、私の将来の参考のためにそれを投稿することも考えました。
リンクから/ http://www.learncpp.com/cpp-tutorial/115-inheritance-and-access-specifiers/
class Base
{
public:
int m_nPublic; // can be accessed by anybody
private:
int m_nPrivate; // can only be accessed by Base member functions (but not derived classes)
protected:
int m_nProtected; // can be accessed by Base member functions, or derived classes.
};
class Derived: public Base
{
public:
Derived()
{
// Derived's access to Base members is not influenced by the type of inheritance used,
// so the following is always true:
m_nPublic = 1; // allowed: can access public base members from derived class
m_nPrivate = 2; // not allowed: can not access private base members from derived class
m_nProtected = 3; // allowed: can access protected base members from derived class
}
};
int main()
{
Base cBase;
cBase.m_nPublic = 1; // allowed: can access public members from outside class
cBase.m_nPrivate = 2; // not allowed: can not access private members from outside class
cBase.m_nProtected = 3; // not allowed: can not access protected members from outside class
}
これは基本的に、派生クラス内の基本クラスのパブリックメンバーおよび保護されたメンバーのアクセス保護です。パブリック継承では、派生クラスはベースのパブリックメンバーと保護メンバーを見ることができます。私的な継承では、それはできません。 protectedでは、派生クラスとそれから派生したクラスはそれらを見ることができます。