为什么C++中析构函数会运行两次?

4

在完成我的编程作业时,我似乎在基本的C++概念上遇到了困难。我发现我的程序中有一个错误,这个错误是由我的析构函数运行次数超过预期引起的。以下是演示我犯错的代码示例,只保留最基本的部分。

#include <iostream>
using namespace std;

class A
{
public:
    A(int num)
    {
        number = num;
        cout << "A constructed with number " << number << ".\n";
    }
    ~A()
    {
        cout << "A destructed with number " << number << ".\n";
    }
private:
    int number;
};

class B
{
public:
    B(A pa)
        : a(pa)
    {
        cout << "B constructor run.\n";
    }
    ~B()
    {
        cout << "B destructor run.\n";
    }
private:
    A a;
};


int main()
{
    A foo(7);
    {
        B bar(foo);
    }
    //Pause the program.
    system("pause");
}

我期望发生的是,A foo(7); 为名为 fooA 对象在堆栈上分配空间并调用构造函数,将 7 传递给构造函数。它将 7 赋值给 number 并打印输出表明构造函数已运行。现在,B bar(foo); 为名为 barB 对象在堆栈上分配空间并调用构造函数,通过值传递 foo,这只是一个包含 int 的容器。构造函数将传递给它的 A 参数赋值给自己的私有数据成员 a,并在屏幕上打印输出。

现在,当 bar 在闭合大括号处超出范围时,我期望调用 bar 的析构函数,该函数将在屏幕上打印输出,然后调用其数据成员的析构函数,即 A a。该析构函数将在屏幕上打印输出,并丢弃其所包含的 int number

我期望输出应该是:

A constructed with number 7.
B constructor run.
B destructor run.
A destructed with number 7.
//Destructors should be called in the reverse order of their construction right?

实际输出:
A constructed with number 7.
B constructor run.
A destructed with number 7. //This is unexpected.
B destructor run.
A destructed with number 7.

是什么导致了这额外的破坏?

3
也要制作吵闹的复制构造函数。 - Benjamin Lindley
请查看“三法则”(Rule of Three):https://dev59.com/eG855IYBdhLWcg3wvXDd - rlduffy
1
可能是为什么类的析构函数会被调用两次?的重复问题。 - Martin York
可能是为什么类的析构函数会被调用两次?的重复问题。 - jww
4个回答

3

你的B构造函数使用值传递方式接收A对象,这意味着foo被复制到参数pa中,然后再复制到成员变量a中。编译器已经成功省略了其中一个复制(作为构造函数的参数),但另一个不能省略,因此会产生第二个需要销毁的A对象。


2

显然,它来自类B的成员数据A a; 我猜你的疑惑是为什么没有看到任何A的构造输出,因为它使用了类A的默认复制构造函数进行构造,最好为类A添加一个复制构造函数,这样你就能看到构造过程了。

A(const A& a)
{
     number = a.number;
     cout << "A copy-constructed with number " << number << ".\n";
}

1

我讨厌人们在使用“foo”和“bar”时变得可爱。

但是,我看到"A"被构建了两次——一次是显式的,另一次是由"B"隐式构建的。还有两个析构函数被调用。

这张图片有什么不喜欢的呢;)?


0

因为在类B中有一个成员A a,所以要销毁B对象,首先要调用它的成员析构函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接