指向派生 class 的指针是否首先创建基 class?

Does pointer to derived class create the Base class first or not?

我有以下代码

#include <iostream>
using namespace std;

class B{
    int i;
    public:
    B(){
        cout << "Constructing B\n";
    }
    void print(){
        cout << "Printing from B with size : "<<sizeof(*this)<<endl;
    }
};



class D:public B{
    int i;
    public:
    D(){
        cout << "Constructing D\n";
    }
    void print(){
        cout << "Printing from D with size : "<<sizeof(*this)<<endl;
    }
};


int main(){

    B b;
    b.print();
    D d;
    d.print();

    D* dp;
    dp->print();
}

这给了我以下输出:

Constructing B
Printing from B with size : 4
Constructing B
Constructing D
Printing from D with size : 8
Printing from D with size : 8

那么,当您创建指向派生 class 的指针时,它并没有首先创建基 class 的实例,这是真的吗?我不认为这是真的,因为 D class 的大小就是证据。但它甚至没有调用基础 class 构造函数。谁能解释一下?

指针不创建任何东西。指针只是指针——包含地址的标量对象。您有责任使您的指针指向内存中的正确位置。

在您的示例中,指针 dp 未初始化。你从来没有让它指向任何地方,所以它没有指向任何地方。您尝试调用 dp->print() 会产生未定义的行为。故事结束。

现在你的指针根本没有被初始化,所以尝试使用它会产生未定义的行为。尝试类似的东西:

D *dp = new D; 
dp->print();
delete dp;

或者为了做得更好,比如:

std::unique_ptr<D> dp = std::make_unique<D>();
dp->print();

...并且 unique_ptr 将在超出范围时自动删除 D

但是请注意,您已将 print 定义为非虚函数,因此调用的函数将取决于所使用的指针(或引用)类型,不是它引用的对象的类型。另外,你还没有定义虚拟dtor。

因此,如果您要执行以下操作:

B *p = std::make_unique<D>();
p->print(); // would invoke B::print, even though the object is a D

...当它超出范围时,它会被错误地销毁,所以你会得到未定义的行为。要更正此问题,您需要将 B 更改为如下内容:

class B{
    int i;
    public:
    B(){
        cout << "Constructing B\n";
    }
    virtual void print(){
        cout << "Printing from B with size : "<<sizeof(*this)<<endl;
    }
    virtual ~B() = default;
};

当您执行此操作时,答案是 "yes"--当您创建派生对象时,它将首先调用基础 class 的构造函数,然后调用派生对象 class。当派生对象被销毁时,情况正好相反:首先调用派生 class 的 dtor,然后当它完成时,将执行基 class 的 dtor。

您可以使用强大的 多态性 Base 的指针分配给 Derived 对象。这是可能的,因为 Derived 实现了 Base 包含的所有内容。因此,隐含的是 Derived 底层在它自己的实例化过程中实例化了 Base

class Base
{
public:
    Base()
    {}
}

class Derived : public Base
{
public:
    Derived()
    {}
}

Derived *derived = new Derived();
Base *base = derived;

在派生class之前构造基class。您的输出反映了这一点。看看这个

//Constructs Base class
Constructing B
//Constructs Derived class
Constructing D
//Prints from derived
Printing from D with size : 8

您的代码在不调用构造函数的情况下打印两次 Printing from D with size : 8 的原因是您的代码从未创建 D.

的第二个实例
D* d;
^^^^ Declares a pointer to a D, Does not create a D!

当您调用 d->print(); 时,它是未定义的行为,因为 d 没有指向 D 的实例。最后,您的代码会打印一个在编译时确定的值(sizeof(D) 是一个编译时值)并且不会触及您的代码运行时的 this 指针。

请参阅 sizeof here 的文档。