#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
。虽然a
是class A
的私有成员变量,但不会继承,但似乎class B
的每个对象都有a
的副本。你能让我知道这里发生了什么吗?
a_p
的主要集class B
中的第6个语句为25. displayB()
函数显示a_p
的{{1}}的值和class B
函数显示displayAP()
的{{1}}的值。但是,两者的输出都是25.你可以解释这部分吗?
答案 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
,其中包含B
和C
。 E
有一个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
中显示A
,C
或D
protected
个成员在整个继承层次结构中都可见。在b
,A
和C
中可以看到D
(但不在B
中,因为它不会从A
继承)。 e
和C
中可以看到D
。public
成员为世界所见。 c
和d
随处可见。在类中声明的所有成员都可以看到其包含类可见的任何成员。使用您的示例,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的成员,您必须明确指定您要这样做。