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

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

本文关键字:创建 基类 是否 指针 派生      更新时间:2023-10-16

>我有以下代码

#include <iostream>
using namespace std;
class B{
    int i;
    public:
    B(){
        cout << "Constructing Bn";
    }
    void print(){
        cout << "Printing from B with size : "<<sizeof(*this)<<endl;
    }
};

class D:public B{
    int i;
    public:
    D(){
        cout << "Constructing Dn";
    }
    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

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

指针

不会创建任何内容。指针只是指针 - 包含地址的标量对象。你有责任使指针指向内存中的正确位置。

在示例中,指针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 Bn";
    }
    virtual void print(){
        cout << "Printing from B with size : "<<sizeof(*this)<<endl;
    }
    virtual ~B() = default;
};
执行此操作时,

答案是"是"——当您创建派生对象时,它将首先调用基类的 ctor,然后调用派生类的 ctor。当派生对象被销毁时,情况正好相反:首先将调用派生类的 dtor,然后在它完成时将执行基类的 dtor。

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

//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文档。

您可以使用

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

class Base
{
public:
    Base()
    {}
}
class Derived : public Base
{
public:
    Derived()
    {}
}
Derived *derived = new Derived();
Base *base = derived;