【问题标题】:How are objects from derived classes constructed in C++如何在 C++ 中构造派生类的对象
【发布时间】:2021-12-28 22:18:17
【问题描述】:

我不是 C++ 专业人士,我主要从事 Java 和 C#。今天老师说了一句话让我很困惑。我试图通过做一些研究来验证信息,但我最终更加困惑。

假设我有 A 类和 B 类。A 类是基类,B 是从 A 派生的。

现在我已经知道,当 B 类的对象被创建时,A 类的构造函数被调用,然后 B 类的构造函数被调用。同样,当 B 类的对象被销毁时,会调用 B 类的析构函数,然后调用 A 类的析构函数。

到目前为止,我的理解是 B 类包含 A 类的所有内容,除了它的构造函数和析构函数。我认为当构建 B 类的对象时,在内存中只创建了一个对象(A 和 B 类型)。

现在我的老师说,当构建 B 时,会创建 2 个单独的对象并以某种方式“链接”在一起:A 类之一,然后 B 类之一。这两个对象都将存在于内存中,直到 B 类销毁对象被调用。那么 B 类对象会在 A 类对象被销毁之前被销毁。

哪个是正确的?

抱歉,如果我的英语一般,那不是我的母语...

编辑:我试着改写一下:

我认为:B 类包含 A 类的所有属性和方法。当我从 B 类创建对象时,内存中只存在 1 个对象。调用 A 类的构造函数只是为了初始化我的对象中最初来自 A 类的部分。

老师说:当我从B类创建一个对象时,在内存中创建了2个对象。当我命令销毁我的对象时,首先销毁内存中的 B 类对象,然后销毁同样保留在内存中的 A 类对象。老师始终无法阐明 B 类对象如何能够使用 A 类对象的方法和属性。

对我来说,这似乎也暗示着内存中某处存在一个我不知道且我几乎无法控制的“幽灵对象”。

【问题讨论】:

  • 您说您的老师说过,创建了 2 个单独的对象并以某种方式“链接”。不知道“你的老师”是什么意思……听起来肯定不对。你能澄清一下吗?
  • 如果您将 A 类重命名为“AppleCore”,将 B 类重命名为“Apple”,而不是“A”和“B”,这可能会有所帮助。现在,当您创建一个 Apple 对象(它是 AppleCore 的子类)时,您正在隐式创建一个 AppleCore 作为 Apple 的第一个/最里面的部分。
  • 旁注:The C++ definition of an object 如果你是从其他语言进来的,那就有点奇怪了。
  • 从这里阅读 Subobjects 部分 - en.cppreference.com/w/cpp/language/object "...一个对象可以包含其他对象,在这种情况下,包含的对象嵌套在前者中对象。一个对象 a 嵌套在另一个对象 b 中,如果...""...一个对象可以有子对象。这些包括...基类子对象..."
  • 我想说你们俩都是对的。两个对象可以重叠(子对象,根据@RichardCritten 的引用)。 “链接”通常是隐含的(B 知道 A 所在的偏移量),除非它不是(虚拟继承)。

标签: c++ inheritance object-storage object-construction object-destruction


【解决方案1】:

根据http://www.vishalchovatiya.com/memory-layout-of-cpp-object/#Layout_of_C_Object_With_Inheritance

这两个类:

class X {
    int     x;
    string str;
public:
    X() {}
    virtual ~X() {}
    virtual void printAll() {}
};
class Y : public X {
    int     y;
public:
    Y() {}
    ~Y() {}
    void printAll() {}
};

将在内存中表示,因此 Y 的内存布局包含基类的数据成员,然后是派生类的数据成员:

      |                              |          
      |------------------------------| <------ Y class object memory layout
      |          int X::x            |
stack |------------------------------|
  |   |              int string::len |
  |   |string X::str ----------------|
  |   |            char* string::str |         
 \|/  |------------------------------|      |-------|--------------------------|
      |           X::_vptr           |------|       |       type_info Y        |
      |------------------------------|              |--------------------------|
      |          int Y::y            |              |    address of Y::~Y()    |
      |------------------------------|              |--------------------------|
      |               o              |              | address of Y::printAll() |
      |               o              |              |--------------------------|
      |               o              |              
------|------------------------------|--------
      |           X::X()             | 
      |------------------------------|       |   
      |           X::~X()            |       |
      |------------------------------|       | 
      |         X::printAll()        |      \|/ 
      |------------------------------|  text segment
      |           Y::Y()             |
      |------------------------------|
      |           Y::~Y()            |
      |------------------------------|
      |         Y::printAll()        |
      |------------------------------|
      |      string::string()        |
      |------------------------------|
      |      string::~string()       |
      |------------------------------|
      |      string::length()        |
      |------------------------------|
      |               o              |
      |               o              |
      |               o              |
      |                              |

同一页面还有其他场景的内存布局,包括多重继承和虚拟继承。

【讨论】:

  • 漂亮的图形。唯一缺少的是指向虚函数表的指针。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-16
  • 1970-01-01
  • 2011-02-21
  • 2023-04-07
相关资源
最近更新 更多