違いは何ですか
public
、private
とprotected
で継承C ++は?
私がSOで見つけた質問はすべて、特定のケースを扱っています。
違いは何ですか
public
、private
とprotected
で継承C ++は?
私が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
。SomeBase
がtypeの匿名メンバーを構成するためのハードコーディングされた方法と同じように機能するもう1つのケースですSomeBase
。これは、他のメンバーと同様に、外部アクセスに対して同じ制御を行うアクセス指定子を持っています。
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が含まれています。アクセスの問題です。
保護された継承とプライベート継承の使用法については、こちらをご覧ください。
継承の可視性を制限すると、一部のクラスが別のクラスを継承していることがコードで認識できstatic_cast
なくなります。派生からベースへの暗黙的な変換は機能せず、ベースから派生への暗黙的な変換も機能しません。
クラスのメンバー/友達だけがプライベート継承を見ることができ、メンバー/友達と派生クラスだけが保護された継承を見ることができます。
公的遺産
IS-A継承。ボタンはウィンドウであり、ウィンドウが必要な場所ならどこでもボタンを渡すことができます。
class button : public window { };
保護された継承
期間内に保護されます。めったに役に立ちません。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!
}
};
個人相続
条件付きで実装。基本クラスの使用法は、派生クラスを実装するためだけのものです。トレイトとサイズが重要な場合に役立ちます(関数のみを含む空のトレイトは、空の基本クラスの最適化を利用します)。多くの場合、封じ込めがより良い解決策です。文字列のサイズは重要なので、ここでよく見られる使用法です
template<typename StorageModel>
struct string : private StorageModel {
public:
void realloc() {
// uses inherited function
StorageModel::realloc();
}
};
公会員
集計
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);
};
プライベートメンバー
実装の詳細を保持する
class window {
private:
int width;
};
Cスタイルのキャストでは、意図的に派生クラスを定義済みの安全な方法で保護されたまたはプライベートの基本クラスにキャストし、他の方向にもキャストできることに注意してください。コードを実装の詳細に依存させる可能性があるため、これは絶対に避けてください。ただし、必要に応じて、この手法を利用できます。
これら3つのキーワードは、完全に異なるコンテキストで使用され、可視性継承モデルを指定します。
この表は、サブクラスが完全に定義された場合にコンポーネントへのアクセスを示すコンポーネント宣言と継承モデルの可能な組み合わせをすべてまとめたものです。
上記の表は次のように解釈されます(最初の行を見てください)。
コンポーネントがpublicとして宣言され、そのクラスがpublicとして継承される場合、結果のアクセスはpublicになります。
例:
class Super {
public: int p;
private: int q;
protected: int r;
};
class Sub : private Super {};
class Subsub : public Sub {};
変数の結果のアクセスp
、q
、r
クラスのSubsubはありませんなし。
もう一つの例:
class Super {
private: int x;
protected: int y;
public: int z;
};
class Sub : protected Super {};
変数について得られたアクセスy
、z
クラス内のサブされる保護及び変数のx
ではないなし。
より詳細な例:
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
メソッドにアクセスできないというエラーメッセージが表示されます。どうして?
可視性指定子を省略すると、コンパイラーはいわゆるプライベート継承を適用するものと想定します。つまり、すべてのパブリックスーパークラスコンポーネントはプライベートアクセスに変わり、プライベートスーパークラスコンポーネントにはまったくアクセスできなくなります。したがって、サブクラス内で後者を使用することはできません。
以前に使用したアクセスポリシーを保持することをコンパイラに通知する必要があります。
class Sub : public Super { };
誤解しないでください。それは、スーパークラスのプライベートコンポーネント(ストレージ変数など)が多少不思議な方法でパブリックコンポーネントになることを意味しません。プライベートコンポーネントが残る民間、公共の ままになります公共。
Sub
クラスのオブジェクトは、クラスから作成された古い兄弟と「ほぼ」同じことを行う場合がありますSuper
。「ほぼ」というのは、サブクラスであるという事実は、クラスがスーパークラスのプライベートコンポーネントにアクセスできなくなったことも意味します。Sub
ストレージ変数を直接操作できるクラスのメンバー関数を記述することはできません。
これは非常に深刻な制限です。回避策はありますか?
はい。
3番目のアクセスレベルは保護と呼ばれます。保護されたキーワードは、それでマークされたコンポーネントがサブクラスのいずれかで使用された場合にパブリックコンポーネントのように動作し、残りの部分ではプライベートコンポーネントのように見えることを意味します。- これは、パブリックに継承されたクラス(この例のスーパークラスなど)にのみ当てはまります -
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つの重要なことを実行します。スーパークラスからストレージ変数にアクセスします。
変数がプライベートとして宣言されている場合、それは不可能です。メイン関数スコープでは、変数はとにかく隠されたままなので、次のように記述した場合:
object.storage = 0;
コンパイラはそれがであることを通知しますerror: 'int Super::storage' is protected
。
最後に、最後のプログラムは次の出力を生成します。
storage = 101
これは、基本クラスのパブリックメンバーが派生クラスから公開される方法に関係しています。
litbが指摘するように、パブリック継承は、ほとんどのプログラミング言語で見られる従来の継承です。つまり、「IS-A」関係をモデル化しています。プライベート継承は、C ++に固有のAFAIKのようなもので、「条件付きで実装された」関係です。つまり、派生クラスでパブリックインターフェイスを使用したいが、派生クラスのユーザーがそのインターフェイスにアクセスできないようにする必要があります。この場合、基本クラスを集約する必要があると多くの人が主張しています。つまり、基本クラスをプライベートベースとして持つ代わりに、基本クラスの機能を再利用するために、派生クラスのメンバーを作成します。
Member in base class : Private Protected Public
継承タイプ : オブジェクトは次のように継承されます:
Private : Inaccessible Private Private
Protected : Inaccessible Protected Protected
Public : Inaccessible Protected Public
1)公的継承:
a。Baseクラスのプライベートメンバーは、Derivedクラスではアクセスできません。
b。Baseクラスの保護されたメンバーは、Derivedクラスで保護されたままです。
c。Baseクラスのパブリックメンバーは、Derivedクラスでパブリックのままです。
したがって、他のクラスは、Derivedクラスオブジェクトを介してBaseクラスのパブリックメンバーを使用できます。
2)保護された継承:
a。Baseクラスのプライベートメンバーは、Derivedクラスではアクセスできません。
b。Baseクラスの保護されたメンバーは、Derivedクラスで保護されたままです。
c。BaseクラスのパブリックメンバーもDerivedクラスの保護されたメンバーになります。
したがって、他のクラスは、Derivedクラスオブジェクトを介してBaseクラスのパブリックメンバーを使用できません。ただし、それらはDerivedのサブクラスで使用できます。
3)プライベート継承:
a。Baseクラスのプライベートメンバーは、Derivedクラスではアクセスできません。
b。Baseクラスの保護メンバーとパブリックメンバーは、Derivedクラスのプライベートメンバーになります。
そのため、Baseクラスのメンバーは、Derivedクラスでプライベートであるため、Derivedクラスオブジェクトを介して他のクラスからアクセスできません。そのため、Derivedクラスのサブクラスでもアクセスできません。
パブリック継承はIS-A関係をモデル化します。と
class B {};
class D : public B {};
すべてD
が B
です。
プライベート継承は、IS-IMPLEMENTED-USING関係(またはそれと呼ばれるもの)をモデル化します。と
class B {};
class D : private B {};
a D
はではありませんがB
、実装でD
はすべてを使用B
します。代わりに包含を使用することにより、プライベート継承を常に排除できます。
class B {};
class D {
private:
B b_;
};
これD
も、を使用してB
、この場合はを使用して実装できますb_
。封じ込めは、継承よりもタイプ間の緊密な結合ではないため、一般的には優先する必要があります。プライベート継承の代わりに包含を使用することは、プライベート継承ほど便利ではない場合があります。多くの場合、それは怠惰であることの怠惰な言い訳です。
誰もがprotected
継承モデルを知っているとは思いません。少なくとも説得力のある説明はまだ見ていません。
D
からプライベートに派生した場合D
、の仮想関数をオーバーライドできますB
。(たとえば、B
オブザーバーインターフェイスの場合、それD
を実装して、this
誰もがD
オブザーバーとして使用することなく、多くのインターフェイスを必要とする関数に渡すことができます。)また、を実行するD
ことにより、のメンバーを選択的B
にそのインターフェイスで使用できるようにすることができますusing B::member
。どちらがB
メンバーであるかを実装するのは構文的に不便です。
protected
継承はvirtual
基本クラスとprotected
ctorで便利だとわかりました:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
別のクラスからパブリックに継承した場合、誰もがあなたが継承していることを知っているので、基本クラスポインターを介して誰でも多態的に使用できます。
保護された方法で継承すると、子クラスだけが多態的に使用できます。
プライベートに継承した場合、自分だけが親クラスのメソッドを実行できます。
これは基本的に、親クラスとの関係について残りのクラスが持つ知識を象徴しています
保護されたデータメンバーは、クラスから継承するすべてのクラスからアクセスできます。ただし、プライベートデータメンバーはできません。次のものがあるとします。
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の例に基づいて...千語に値する小さなテーブルだと思います:)
私は簡単な答えを見つけたので、将来の参考のために投稿することも考えました。
リンク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
}