【问题标题】:How can be that a destructor is called but no constuctor before that?怎么可能调用了析构函数但在此之前没有构造函数?
【发布时间】:2020-08-16 07:19:35
【问题描述】:

我目前正在学习 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 析构函数呢?那里发生了什么?

【问题讨论】:

  • 复制构造函数被调用,它不打印任何东西,因为它只是默认值。
  • 我还不太明白。那里需要一个复制构造函数?
  • 将 whoisthis3 例程更改为:void whoisthis3(Person&amp; object)
  • 我知道,该功能有 3 个变体,其中之一就是您提到的。不过我这里没有包括其他的。
  • 然后为你的类实现一个拷贝构造函数和赋值运算符

标签: c++ oop destructor copy-constructor virtual-destructor


【解决方案1】:

考虑以下简单程序:

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

【讨论】:

  • 啊,现在我意识到这些对象实际上与普通旧变量发生了同样的事情。现在这完全有道理。
  • 好,很高兴你现在明白了 :)
  • 但是为什么编译器有这个功能呢?那么为什么它不调用普通的构造函数呢?
  • 因为从另一个相同类型的对象创建对象通常很有用。此功能可让您轻松做到这一点。
  • @Julius_Evola • 复制构造函数是一个普通的构造函数。 (即使是编译器为你合成的。)
【解决方案2】:
void whoisthis3(Person object)
{
    object.introduce();
}

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


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

【讨论】:

  • 好的,所以对象被视为变量。但是为什么要复制构造函数呢?该函数可以调用普通的构造函数。
  • @Julius_Evola 因为它需要制作对象的副本。为此,您需要访问正在复制的对象。
猜你喜欢
  • 2013-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-09
  • 2014-09-18
  • 2011-04-16
  • 1970-01-01
  • 2017-12-21
相关资源
最近更新 更多