怎么可能调用了析构函数而在此之前没有调用构造函数呢?

How can be that a destructor is called but no constuctor before that?

我目前正在学习c++的面向对象特性。我写了一段代码来测试继承和多态是如何工作的。 这是代码的一部分:

    class Person
{
    public:
    Person()
    {
        cout << "Person constructed\n";
    }

    virtual void introduce()
    {
        cout << "hi from person" << endl;
    }

    ~Person()
    {
        cout << "Person destructed\n";
    }
};

class Student : public Person 
{
    public:
    Student()
    {
        cout << "Student constructed\n";
    }

    void introduce()
    {
        cout << "hi from student" << endl;
    }

    ~Student()
    {
        cout << "Student destructed\n";
    }
};

class Farmer : public Person 
{
    public:
    Farmer()
    {
        cout << "Farmer constructed\n";
    }

    void introduce()
    {
        cout << "hi from farmer" << endl;
    }

    ~Farmer()
    {
        cout << "Farmer destructed\n";
    }
};

class SJW : public Student
{
    public:
    SJW()
    {
        cout << "SJW constructed\n";
    }

    ~SJW()
    {
        cout << "SJW destructed\n";
    }
};

void whoisthis3(Person object)
{
    object.introduce();
}

int main()
{
    Student mark;
    SJW bigred;
    Farmer max;

    cout << endl;

    whoisthis3(mark);
    whoisthis3(max);
    whoisthis3(bigred);

    cout << endl;

    return 0;
}

这是它产生的输出:

Person constructed
Student constructed
Person constructed
Student constructed
SJW constructed
Person constructed
Farmer constructed

hi from person
Person destructed
hi from person
Person destructed
hi from person
Person destructed

Farmer destructed
Person destructed
SJW destructed
Student destructed
Person destructed
Student destructed
Person destructed

这些派生类按照预期在开始和结束时构造和销毁。但是whoisthis3方法执行的时候怎么会调用Person的析构函数呢?那里发生了什么?

void whoisthis3(Person object)
{
    object.introduce();
}

此函数按值接受其 object 参数。这意味着将创建一个新的 Person 对象作为您传递给 whoisthis3 的对象的副本。由于您没有显式定义 Person 的复制构造函数,因此它使用编译器生成的默认复制构造函数。当函数结束时,它的 object 参数被销毁,你会看到它的析构函数打印的语句。


请注意,正如您所观察到的,whoisthis3 将始终调用 Person::introduce,因为 object 是一个 Person,而不是从它派生的任何类型。这通常称为 object slicing.

考虑以下简单程序:

struct A 
{
    A() { std::cout << "construct A\n";  }
    ~A() { std::cout << "destruct A\n";  }  
};

int main()
{
  A a;
}

这会打印:

constructs A
destructs A

符合预期。

现在让我们添加一个简单的函数:

void f(A a) {}

并称它为:

int main()
{
  A a;
  f(a);
}

我们看到:

construct A
destruct A
destruct A

那么额外的 destruct A 是从哪里来的?相应的 construct A 在哪里?答案是,当您调用 f 时,会调用默认的复制构造函数。如果你在复制构造函数中打印东西,像这样:

A(A const&) { std::cout << "copy-construct A\n"; }

然后你得到输出:

construct A
copy-construct A
destruct A
destruct A

这表明确实构建了 2 个对象,然后都被销毁了。

另一方面,如果 f 像这样通过引用获取参数:

void f(A& a) {}

然后不调用复制构造函数(因为没有复制),输出将是:

construct A
destruct A