子类真的继承私有成员变量吗?

12
基本上,据我所知,当您创建一个具有公共、受保护和私有部分以及每个公共和受保护部分中的变量/函数的基类时,公共和受保护部分中的变量/函数将继承到子类的相应部分中(由class subclass:private base定义,其中将获取base的所有公共和受保护成员并将它们放入public中,将单词private更改为public将它们全部放入public中,将其更改为protected将它们全部放入protected中)。
因此,当您创建一个子类时,您永远不会从先前类(在这种情况下是基类)的私有部分收到任何内容,如果这是真的,那么子类的对象应该从未拥有来自基类的私有变量或函数,对吗?
让我们举个例子:
#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.
}

现在,根据我的理解,这应该打印出: 15 15 因为它总是使用myClass函数的版本(因此使用myClass myVariable)。但是,奇怪的是,情况并非如此。运行此程序的结果打印: 15 0 这让我想知道,我们不仅继承了myVariable,还能否对其进行修改?显然,这样会创建myVariable的另一个版本,否则myClass版本就不会为0。通过这样做,我们确实正在编辑第二个副本的myVariable。 请有人向我解释这一切,这已经摧毁了我对继承的理解。

请问您能否修正一下代码的缩进? - Oliver Charlesworth
1
myObject.getMyVariable() 是未定义的行为。 - chris
1
你从未调用过 myObject.setMyVariable。所以这只是一个简单的编程错误,与继承无关! - Oliver Charlesworth
我认为人们可能会对类和对象之间的区别感到困惑,并误解这种区别在使用继承后就会消失的想法。 - us2012
7个回答

20
基本上据我所知,当你创建一个带有公共、保护和私有部分以及在每个公共和保护部分中具有变量/函数的基类时,这些公共和保护部分的变量/函数会被继承到子类的适当部分(由class subclass : private base定义,它将获取所有base的公共和私有成员并将它们放入public中,将单词private更改为public将把它们全部放入public,将其更改为protected将所有内容都放入protected中)。
这种说法有点混乱。
请注意,C++中的继承是针对类和结构体定义的。单个对象(即实例)不会从其他对象继承。使用其他对象构建对象称为组合。
当一个类从另一个类继承时,它会获得来自那个类的所有内容,但是继承的字段的访问级别可能会限制它们在继承者内部的使用。
此外,类有3种继承方式:private(默认情况下),protected和public。每种方式都会在子类继承时更改类属性和方法的访问级别。
如果我们按照以下方式排序访问级别:public,protected,private(从最不受保护到最受保护),则可以将继承修饰符定义为将继承类字段的访问级别提高到至少它们在派生类(即继承类)中指定的级别。
例如,如果类B使用protected继承修饰符从类A继承:
  class B : protected A { /* ... */ };

那么,来自A的所有字段至少在B中具有protected级别:

  • public字段变成protectedpublic级别提升为protected),
  • protected字段保持protected(相同的访问级别,因此无需修改),
  • private字段保持private(访问级别已经高于修饰符)

哇,非常感谢这篇有用的文章。那解释得非常清楚!(真的非常感谢大家,但这篇文章得到了最佳答案) - TorbenC

10

当你创建一个子类时,你从未收到来自[基类]的私有部分的任何内容。如果这是真的,那么子类的对象就不应该有自己版本的来自基类的私有变量或函数,对吗?

不对。派生类继承了基类的所有成员,包括私有成员。继承的类的对象具有这些私有成员,但没有直接访问它们的权限。它可以访问基类的公共成员,这些成员可能会访问这些私有成员,但是它(派生类)可能没有新的成员函数以此类访问权限:

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
  }
};

有趣,那么你可以通过另一种方法访问继承的私有成员吗?我的意思是,我能以任何方式访问yourClass中的myVariable吗?我明白你的意思,但是getMyVariable不会只访问基类的myVariable版本而不是访问yourclass版本吗?我已经阅读过了,在子类中无法使用任何继承到的私有成员变量(在你的帖子之后进行了更多研究)。 - TorbenC
你不能在子类的实现中使用它们。当然,你可以在基类的实现中使用它们,并从外部调用基类的公共方法/访问公共字段。 - us2012
没错,但我的意思是,你如何访问和修改它继承的子类版本的私有成员? - TorbenC
1
私有成员只有一个版本。子类从基类继承该成员,不存在第二个副本。在幕后,实际访问和修改的是基类的版本。(这并不意味着您的 yourObject 在某种程度上正在访问 myObject,它们是不同的对象!) - us2012
啊,所以这类似于继承公共函数,你可以访问它(虽然在这种情况下不是直接访问),但是除非覆盖它,否则你不会获得自己的版本? - TorbenC

7

myObjectyourObject 是两个不同的对象!为什么它们要共享任何东西呢?

这样考虑:忘记继承,假设你有一个类 Person,它有 private int age;public void setAge(int age) {...}。然后你创建了两个对象:

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

你会预计Bill现在也是35岁吗?显然不会。同样地,你的myObject不会与yourObject共享数据。


针对你的评论:

yourClass继承自myClass。这意味着yourObjectmyObject都有它们自己的myVariable,后者显然根据定义,前者则是从myClass继承来的。


哦!!!! 难怪,这是两个不同的对象,所以才会这样!每个对象都有自己的函数实例等。那么我的理解是,myVariable从未被继承?我只是想澄清一下,我的继承理解正确吗? - TorbenC
它是继承的,参见Beta的答案! - us2012
非常感谢您的小贡献,现在一切都很清楚了。也谢谢你! - TorbenC

6
从���理上讲,基类的每个成员(包括成员函数)都进入了子类中。无论它们是私有的还是公开/保护/私有继承,都没有关系。因此,在您的示例中,yourClass 包含 getMyVariable()setMyVariable()myVariable 这三个成员。这一切都很简单,好吗?
重要的是我们如何访问它们。就像在您的系统上删除文件一样。因此,您应该首先了解成员不存在和成员存在但不可访问之间的区别。暂时假设所有继承都是公共的。然后,基类的所有公共成员在派生类中是公共的,保护成员是受保护的,私有成员是不可访问的。它们是不可访问而不是不存在,因为基类中可能有一些位于保护和公共部分中的成员函数,这些成员函数访问基类的私有成员。因此,我们需要所有这些由基类的公共和受保护成员函数访问的私有成员,以实现它们的功能。由于我们无法简单地确定哪个成员函数需要哪个成员,因此我们将基类的所有私有成员包含在派生类中。所有这些只意味着在派生类中,私有成员只能通过基类的成员函数进行修改。
注意:每个私有成员必须由公共/保护成员函数直接或间接访问[通过另一个由公共/保护成员函数调用的私有成员函数],否则它没有用处。
因此,我们现在知道基类的私有成员变量在派生类中有用,即用于其公共/受保护成员函数的功能。但是它们不能在基类中直接访问。
现在,我们将注意力转向私有/公共继承。对于公共继承,它意味着基类的所有可访问成员(即公共和保护成员)不能比公共级别更具有包容性。由于公共是最具包容性的级别,因此公共和保护成员仍然是公共的。但是对于保护和私有继承,它们在派生类中分别变为受保护和私有。在后一种情况下,由于所有这些成员都是私有的,它们无法在层次结构链中进一步访问,但仍然可以被给定的派生类访问。
因此,在派生类中,每个基类成员的级别都是它们在派生类中的级别()和继承类型(公共/保护/私有)的较小值。
对于类外的函数,相同的概念也适用。对于它们来说,私有和受保护成员是不可访问的,但它们确实存在,并且可以通过公共成员函数访问。
以您的情况作为最后一个例子,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*/
};

此外:您也可以对继承类型添加异常情况,例如,私有继承除了在派生类中公开声明的成员。但这是另一个完全不同的问题。


非常好的答案!非常清晰,覆盖了问题的每个方面。 - Jatin

5

您从未调用myObject.setMyVariable(),因此myObject.getMyVariable()将不会返回15。

private并不意味着static


嗯,它可能会……不过我知道你的意思 :) - chris
这就是这个问题的关键,为什么yourObject.getMyVariable返回15?这意味着存在两个myVariable实例,否则无论使用哪个对象进行设置/获取,都将在两个对象上返回15。显然,有两个myVariables的存在,这就是问题所在。 - TorbenC

0

之后:

class yourClass : public myClass {};

仍然只有一个成员变量。但是有两种通过名称访问它的方式:myClass::myVariableyourClass::myVariable

在这些表达式中,类名被称为命名类。了解的第二个关键点是访问权限适用于命名类和成员名称的组合;不仅适用于成员名称,也不适用于变量本身。

如果提到成员而没有显式地出现命名类,则从命名该成员的左侧表达式的类型推断出命名类(如果没有这样的表达式,则暗示为this->)。

此外,实际上有四种可能的访问类型:publicprotectedprivate无访问。你不能将成员声明为无访问,但当私有成员被继承时,就会出现这种情况。


将所有的理论应用到你的例子中:

  • myClass::myVariable的名称为private
  • yourClass::myVariable的名称为无访问权限

再次强调,实际上只有一个变量,但它可以用两种不同的方式命名,并且访问权限取决于使用哪个名称。


最后,回到你的原始示例。myObjectyourObject是不同的对象。我认为你想要写的,或者你在脑海中想象的实际上是这种情况:
yourClass yourObject;
myClass& myObject = yourObject;
//    ^^^

这意味着myObject表示yourObject的基类部分。然后执行以下操作:

yourObject.setMyVariable();

变量被设置为15,因此

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

会输出15,因为确实只有一个变量。


-2

这可能有所帮助

#include<iostream>
using namespace std;

class A
{
int b;  
};

class B : private A
{

};

int main()
{
C obj;
cout<<sizeof(obj);  
return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接