遗产实际上如何运作?

时间:2017-02-16 17:17:19

标签: c++ inheritance

#include<iostream>
using namespace std;

class A
{
  int a;
public:
  int a_p;
  void displayA()
  {
    cout<<"A::a "<<a<<endl;
  }
  void displayAP()
  {
    cout<<"A::a_p "<<a_p<<endl;
  }
  void get_a(int x)
  {
    a=x;
  }
};

class B:public A
{
  int b;
public:
  void displayB()
  {
    cout<<"A::a_p "<<a_p<<endl;
  }
};

int main()
{
  B b1,b2;
  b1.get_a(5);
  b2.get_a(10);

  b1.displayA();
  b2.displayA();

  cout<<"......"<<endl;

  b1.a_p=25;
  b1.displayB(); 
  b1.displayAP();
}

我需要澄清以下内容:

  • main下的前5个语句将输出设为5,10。虽然aclass A的私有成员变量,但不会继承,但似乎class B的每个对象都有a的副本。你能让我知道这里发生了什么吗?

  • a_p的主要集class B中的第6个语句为25. displayB()函数显示a_p的{​​{1}}的值和class B函数显示displayAP()的{​​{1}}的值。但是,两者的输出都是25.你可以解释这部分吗?

3 个答案:

答案 0 :(得分:2)

简短回答:继承就像一个Matryoshka玩偶,每个类都完全包含所有基类(如果有的话)。

长答案:当一个类继承自一个或多个其他类时,派生类包含其父类(es),而父类又包含其父类,直到您到达派生程度最低的类为止。 (没有自己的父类的类)。因此,例如,使用此设置:

class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};
class E : public D {};

E包含D,其中包含B(其中包含A)和C(其中包含另一个A );它看起来像这样(使用MSVC生成,使用compiler option /d1reportSingleClassLayoutE中的an online x64 environment)。

class E size(1):
        +---
        | +--- (base class D)
        | | +--- (base class B)
        | | | +--- (base class A)
        | | | +---
        | | +---
        | | +--- (base class C)
        | | | +--- (base class A)
        | | | +---
        | | +---
        | +---
        +---

请注意,对于virtual基类,这种类比稍微偏离,它们往往位于最派生类“主体”之后(因为缺少更好的术语;分配给所有非基数的内存) - 内存中的virtual基类和数据成员。

class A {};
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};
class E : public D {};

E包含D,其中包含BCE有一个A的单个实例,背面是超级的。

class E size(16):
        +---
        | +--- (base class D)
        | | +--- (base class B)
 0      | | | {vbptr}
        | | +---
        | | +--- (base class C)
 8      | | | {vbptr}
        | | +---
        | +---
        +---
        +--- (virtual base A)
        +---

由于每个派生类都包含其整个继承层次结构,因此它还包含在其任何基类中声明的所有变量。

class A { private: int a; protected: int b; public: int c; };
class B { public: int d; };
class C : public A, public B { protected: int e; };
class D : public C {};

static_assert(sizeof(C) == sizeof(A) + sizeof(B) + sizeof(int), "Size mismatch.");
static_assert(sizeof(D) == sizeof(C),                           "Size mismatch.");
static_assert(sizeof(D) == sizeof(int) * 5,                     "Size mismatch.");

D包含C,其中包含A(其中包含3 int s),B(其中包含int),和int。 Clang,GCC或MSVC都不会发出Size mismatch.错误。使用/d1reportSingleClassLayoutD ...

class D size(20):
        +---
        | +--- (base class C)
        | | +--- (base class A)
 0      | | | a
 4      | | | b
 8      | | | c
        | | +---
        | | +--- (base class B)
12      | | | d
        | | +---
16      | | e
        | +---
        +---

因此,访问说明符实际上不会影响继承的内容。然而,他们的影响是派生类可见的。

  • private成员仅在声明它们的类中可见。 a中显示ACD
  • 遇到后,
  • protected个成员在整个继承层次结构中都可见。在bAC中可以看到D(但不在B中,因为它不会从A继承)。 eC中可以看到D
  • public成员为世界所见。 cd随处可见。

在类中声明的所有成员都可以看到其包含类可见的任何成员。使用您的示例,A::displayA()始终可以看到A::a,即使在派生类B的实例上调用时也是如此;但是,如果B声明隐藏displayA()的成员A::displayA(),那么B::displayA()将无法看到A::a,并且必须依赖public {1}}或protected成员A如果想与A::a合作。

class A {
    int a;

  public:
    void displayA() { std::cout << "A::a " << a << std::endl; }
};

class B : public A {
  public:
    // Will emit some variation on "A::a is private, you can't access it here."
    // Note that no compiler will claim that it doesn't exist.
    // void displayA() { std::cout << "A::a " << a << std::endl; }

    // This works, though, since it goes through A::displayA(), which can see A::a.
    void displayA() { return A::displayA(); }
};

答案 1 :(得分:0)

  

displayAP()函数显示A类的a_p值。

您如何确定?b1.displayAP();并非A::displayA()而是B::displayA(),因为b1的类型为B。 (B继承了所有A的属性。)。即便是这一个b1.a_p=25;也会为B::a_p分配值25

  

你能让我知道这里发生了什么吗?

它不是副本,C ++中的继承(在所有语言中也是如此)继承其父级的所有成员,无论成员所在的说明符是什么,并且它们的访问说明符保持不变。

所以,它给你的输出是正确的,特别是在#34;

下的前5个陈述中

您应该阅读有关继承http://www.learncpp.com/cpp-tutorial/112-basic-inheritance-in-c/

的更多信息

答案 2 :(得分:0)

第一个要点你是对的。派生类对象具有称为“基类子对象”的区域。因此,派生类对象包含基类子对象,其方式与包含任何非静态数据成员的成员子对象的方式完全相同。 (但情况并非总是如此,因为编译器会尝试优化事物,并且结果对象可能无法明确区分成员变量以获得非常简单的示例。)

第二个要点你错了。它们指向A :: a_p(A类中包含的属性)。由于B不包含属性a_p,因此它将隐式指向A中的那个。

为了帮助您理解,请考虑以下代码,其中我在A:

中隐藏变量
#include<iostream>

class A {

public:
    int a;
};

class B : public A {
public:
    int a;

    B() = delete;
    B(int a_a, int b_a) {
        A::a = a_a;
        a = b_a;
    } 

    void displayA_A() {
        std::cout << "A::a: " << A::a << std::endl;
    }

    void displayB_A() {
        std::cout << "B::a " << B::a << std::endl;
    }
};

int main() {
    B b(10,20);

    b.displayA_A();
    b.displayB_A();

    return 0;
}

在main中构造B时,首先构造对象A并将其成员设置为10.然后B中的成员将设置为20.

请注意,在此示例中,为了引用A的成员,您必须明确指定您要这样做。