web-dev-qa-db-ja.com

サブクラスは本当にプライベートメンバー変数を継承しますか?

基本的に私が知る限り、パブリックセクション、プロテクトセクション、プライベートセクションを含む基本クラスを作成すると、パブリックセクションとプロテクトセクションのそれぞれの変数/関数は、サブクラスの適切なセクション(クラスサブクラスによって定義される)に継承されます。 :プライベートベース。ベースのすべてのパブリックメンバーと保護されたメンバーを取得してパブリックにします。プライベートからパブリックにWordを変更すると、すべてがパブリックになり、保護に変更すると、すべてが保護されます)。

したがって、サブクラスを作成するときに、前のクラス(この場合は基本クラス)のプライベートセクションから何も受け取らないので、これがtrueの場合、サブクラスのオブジェクトは独自のバージョンの基本クラスのプライベート変数または関数は正しいですか?

例を実行してみましょう:

#include <iostream>

class myClass     // Creates a class titled myClass with a public section and a private section.
{
public:
  void setMyVariable();
  int getMyVariable();
private:
  int myVariable;     // This private member variable should never be inherited.
};

class yourClass : public myClass {};    // Creates a sub-class of myClass that inherits all the public/protected members into  the
// public section of yourClass. This should only inherit setMyVariable()
// and getMyVariable() since myVariable is private. This class does not over-ride any
// functions so it should be using the myClass version upon each call using a yourClass
// object. Correct?

int main()
{
  myClass myObject;           // Creates a myClass object called myObject.
  yourClass yourObject;       // Creates a yourClass object called yourObject
  yourObject.setMyVariable(); // Calls setMyVariable() through yourObject. This in turn calls the myClass version of it    because
  // there is no function definition for a yourClass version of this function. This means that this
  // can indeed access myVariable, but only the myClass version of it (there isn't a yourClass
  // version because myVariable is never inherited).

  std::cout << yourObject.getMyVariable() << std::endl;   // Uses the yourClass version of getMyVariable() which in turn
  // calls the myClass version, thus it returns the myClass myVariable
  // value. yourClass never has a version of myVariable Correct?

  std::cout << myObject.getMyVariable() << std::endl;     // Calls the myClass version of getMyVariable() and prints myVariable.

  return 0;
}

void myClass::setMyVariable()
{
  myVariable = 15;        // Sets myVariable in myClass to 15.
}

int myClass::getMyVariable()
{
  return myVariable;      // Returns myVariable from myClass.
}

さて、理論的には、これは次のように出力されます。1515これにより、常にmyClassバージョンの関数を使用します(したがって、myClass myVariableを使用します)。しかし、奇妙なことに、これは当てはまりません。このプログラムを実行した結果は次のようになります。150これは、実際にはmyVariableを継承するだけでなく、それをいじくり回す機能もあるのだろうかと思います。明らかに、これはmyVariableの代替バージョンを何らかの方法で作成しています。そうでない場合、myClassバージョンに0はありません。実際、これをすべて実行して、myVariableの2番目のコピーを編集しています。

誰かがこれをすべて私に説明してもらえますか、これは私の相続の理解を崩壊させました。

12
TorbenC

基本的に私が知る限り、パブリックセクション、プロテクトセクション、プライベートセクションを含む基本クラスを作成すると、パブリックセクションとプロテクトセクションのそれぞれの変数/関数は、サブクラスの適切なセクション(クラスサブクラスによって定義される)に継承されます。 :プライベートベース。ベースのすべてのパブリックメンバーとプライベートメンバーを取得してパブリックにします。Wordをプライベートからパブリックに変更すると、すべてがパブリックになり、保護に変更すると、すべてが保護されます)。

この声明には少し混乱があります。

継承はC++のクラスと構造体に対して定義されていることを思い出してください。個々のオブジェクト(つまりインスタンス)は他のオブジェクトから継承しません。他のオブジェクトを使用してオブジェクトを構築することをcompositionと呼びます。

クラスが別のクラスから継承する場合、そのクラスからすべてを取得しますが、継承されたフィールドのアクセスレベルにより、継承内での使用が妨げられる場合があります。

さらに、クラスには、private(デフォルト)、protected、およびpublicの3種類の継承があります。それらのそれぞれは、サブクラスによって継承されるときに、クラスのプロパティとメソッドのアクセスレベルを変更します。

publicprotectedprivateのようにアクセスレベルを順序付けすると、保護が最も低いものから最も保護されたものへと、継承修飾子を上げるように定義できます。継承されたクラスフィールドのアクセスレベルは、派生クラス(つまり、継承されたクラス)で少なくとも指定されたレベルになります。

たとえば、クラスBがクラスAからprotected継承修飾子を使用して継承する場合:

  class B : protected A { /* ... */ };

その場合、Aのすべてのフィールドは少なくともprotectedBレベルになります。

  • publicフィールドはprotectedになります(publicレベルはprotectedに上げられます)、
  • protectedフィールドはprotectedのままです(同じアクセスレベルなので、ここでは変更しません)、
  • privateフィールドはprivateのままです(アクセスレベルはすでに修飾子を上回っています)
20
didierc

「サブクラスを作成すると、[基本クラス]のプライベートセクションから何も受信しません。これが当てはまる場合、サブクラスのオブジェクトは、ベースから独自のバージョンのプライベート変数または関数を持つべきではありません。クラス、正しいですか?」

いいえ。派生クラスは、プライベートクラスを含む基本クラスのすべてのメンバーを継承します。継承されたクラスのオブジェクトhasこれらのプライベートメンバーですが、それらへの直接アクセスはありません。基本クラスのパブリックメンバーにアクセスでき、それらのメンバーにアクセスできますが、そのメンバー(派生クラス)には、そのようなアクセス権を持つ新しいメンバー関数がない場合があります。

class yourClass : public myClass
{
public:
  void playByTheRules()
  {
    setMyVariable(); // perfectly legal, since setMyVariable() is public
  }

  void tamperWithMyVariable()
  {
    myVariable = 20; // this is illegal and will cause a compile-time error
  }
};
9
Beta

myObjectyourObjectは2つの異なるオブジェクトです!なぜ彼らは何かを共有する必要がありますか?

そのように考えてください。継承を忘れて、_private int age;_とpublic void setAge (int age) {...}を持つクラスPersonがあるとします。次に、2つのオブジェクトをインスタンス化します。

_Person bob;
Person bill;
bob.setAge(35);
_

ビルも今35歳になると思いますか?そうじゃないでしょう?同様に、myObjectはそのデータをyourObjectと共有しません。


あなたのコメントに応えて:

クラスyourClassmyClassから継承します。つまり、yourObjectmyObjectの両方に独自のmyVariableがあり、後者は明らかに定義上、前者はmyClassから継承されています。

7
us2012

物理的には、基本クラスのすべてのメンバー(メンバー関数を含む)がサブクラスに入ります。プライベートかどうかは関係ありません。あなたがそれらを公的に/保護された-ly/privatelyに継承するかどうかは関係ありません。したがって、この例では、yourClassにはgetMyVariable()setMyVariable()、およびmyVariableの3つすべてが含まれています。これはすべてとても簡単ですいいですね

重要なのは、どのようにそれらにアクセスできるかです。これは、システムでファイルが削除されたときのようなものです。したがって、最初に、メンバーがそこにいないことと、メンバーがそこにいるがアクセスできないことの違いを理解する必要があります。今のところ、すべての継承は公に行われていると仮定します。次に、基本クラスのすべてのパブリックメンバーは派生クラスでパブリックになり、保護されたメンバーは保護され、プライベートメンバーにはアクセスできなくなります。基本クラスのプライベートメンバーにアクセスする基本クラスの保護されたセクションとパブリックセクションにいくつかのメンバー関数が存在する可能性があるため、これらにはアクセスできず、存在しません。したがって、baseのパブリックおよび保護されたメンバー関数によってアクセスされるbaseのすべてのプライベートメンバーが、その機能のために必要です。どのメンバーがどのメンバー関数に必要かを簡単に判断する方法がないため、基本クラスのすべてのプライベートメンバーを派生クラスに含めます。これはすべて、派生クラスでは、基本クラスのメンバー関数を介してのみプライベートメンバーを変更できることを意味します。

注:すべてのプライベートメンバーは、パブリック/保護されたメンバー関数によって直接または間接的に[パブリック/保護されたメンバー関数によって呼び出される別のプライベートメンバー関数を介して]アクセスする必要があります。そうでない場合は使用できません。

したがって、これまで、基本クラスのプライベートメンバー変数は、派生クラスで、つまりパブリック/保護されたメンバー関数の機能のために使用されることがわかっています。ただし、基本クラスで直接アクセスすることはできません。

ここで、私たちは私的/公的継承に注意を向けます。パブリック継承の場合、基本クラスのすべてのアクセス可能なメンバー(つまり、パブリックメンバーと保護されたメンバー)がパブリックよりも寛容なレベルになることはできないことを意味します。パブリックは最も寛容なレベルであるため、パブリックおよび保護されたメンバーはパブリックのままです。ただし、保護継承とプライベート継承では、派生クラスでそれぞれ保護とプライベートになります。後者の場合、これらのメンバーはすべてプライベートであるため、階層チェーン内でそれ以上アクセスすることはできませんが、指定された派生クラスから同じようにアクセスできます。

したがって、派生クラスの各基本クラスメンバーのレベルは、派生クラス()のレベルと継承のタイプ(パブリック/保護/プライベート)のいずれか低い方になります。

同じ概念がクラス外の関数にも当てはまります。それらの場合、プライベートおよび保護されたメンバーにはアクセスできませんが、それらは存在し、パブリックメンバー関数からアクセスできます。

最後の例として、setMyvariable()getMyVariable()は派生クラスのmyVariableにアクセスできます。ただし、派生クラスで指定された関数はmyVariableにアクセスできません。クラスの変更:

class myClass
{
public:
  void setMyVariable();
  int getMyVariable();
private:
  int myVariable;
};

class yourClass : public myClass
{
public:
  // void yourFunction() { myVariable = 1; }
  /*Removing comment creates error; derived class functions can't access myVariable*/
};

さらに:継承のタイプに例外を追加することもできます。派生クラスで公開されたメンバーを除くプライベート継承。しかし、それはまったく別の質問です。

6
Anurag Kalia

myObject.setMyVariable()を呼び出すことはないため、myObject.getMyVariable()は15を返しません。

privatestaticを意味するものではありません。

5

後:

class yourClass : public myClass {};

まだメンバー変数は1つだけです。ただし、名前でアクセスするには、myClass::myVariableyourClass::myVariableの2つの方法があります。

これらの式では、クラス名は名前付けクラスとして知られています。理解すべき2番目の重要なことは、アクセス権は名前付けクラスとメンバー名の組み合わせに適用されます;メンバー名だけでなく、変数自体にも影響しません。

ネーミングクラスが明示的に存在せずにメンバーが言及されている場合、ネーミングクラスは、メンバーに名前を付けた.または->の左側の式のタイプから推測されます(そのような式がない場合はthis->が暗黙指定されます)。

さらに、実際には4つの可能なアクセスタイプがあります:publicprotectedprivate、およびアクセスなし。メンバーにアクセスなしがあると宣言することはできませんが、プライベートメンバーが継承されるとその状況が発生します。


このすべての理論をあなたの例に適用する:

  • myClass::myVariableという名前はprivateです。
  • 名前yourClass::myVariableアクセスなしです。

繰り返しになりますが、実際には変数は1つだけですが、2つの異なる方法で名前を付けることができ、使用する名前によってアクセス権が異なります。


最後に、元の例に戻ります。 myObjectyourObjectは異なるオブジェクトです。あなたが書くつもりだったもの、またはあなたが精神的に想像しているものは、実際にはこの状況だと思います。

yourClass yourObject;
myClass& myObject = yourObject;
//    ^^^

これは、myObjectyourObjectの基本クラス部分に名前を付けることを意味します。後で:

yourObject.setMyVariable();

変数は15に設定されているため、

std::cout << myObject.getMyVariable() << std::endl;

実際には変数が1つしかないため、15を出力します。

0
M.M