C ++:析构函数在它应该超出范围之前被调用?

时间:2012-10-05 13:13:57

标签: c++ destructor

我遇到了在子程序结束时为类调用析构函数的问题,即使它应该在子例程范围之外定义。

以下是我显示问题的最小代码:

#include <iostream>
using namespace std;

class Foo {
private:

    double *array;

public:

Foo(int N) {
   array = new double[N];
   for (int i=0; i<N; i++) {
       array[i]=0;
   }
}

~Foo() {
   delete[] array;
}
};

void subroutine(Foo x) {
   cout << "Hello!" << endl;
}

int main() {
   Foo bar(10);
   subroutine(bar);
   subroutine(bar);
}

现在,对象栏的析构函数在第一个子例程完成后被调用,即使它的范围应该是整个main()函数?这意味着当我调用第二个子例程时,再次调用析构函数并且我得到内存泄漏。

我发现我可以通过在子程序中通过引用调用来解决这个问题,但是我对这个修复不太满意,因为我不明白为什么它首先不起作用。 任何人都可以为我阐明这一点吗?

感谢。

4 个答案:

答案 0 :(得分:21)

您正在将Foo值传递给subroutine函数。这意味着它拥有自己的副本,在退出它的范围时会被销毁。

void subroutine(Foo x) {
   // x is a new Foo here. It will get destroyed on exiting scope,
   // resulting in a destructor call
}

这里的主要问题是你没有实现复制构造函数,因此不会复制动态分配的数组(只有指向它的指针)。因此,当您复制Foo个对象时,每个副本都引用同一个数组。每个副本都会试图破坏它。

您应该遵循rule of three并实现一个赋值操作符和一个复制构造函数,它们可以对数组进行“深层复制”,这样每个Foo对象都拥有自己的数组。

答案 1 :(得分:6)

您将按值传递到子例程,因此正在创建副本。为避免副本通过引用传递它:

void subroutine(Foo& x)
{
    cout << "Hello!" << endl;
}

您可以通过将复制构造函数和复制赋值运算符声明为私有来防止类的意外副本:

class Foo {
private:

    double *array;

    Foo(const Foo&);
    Foo& operator=(const foo&);

public:
    ...
};

然后您会收到编译错误。如果你真的需要能够复制你的课程,那么你实际上需要实现这些功能来执行“深层复制”(或者更好地使用std::vector<float>并让你为你管理内存,包括安全复制)。

答案 2 :(得分:3)

当您调用void subroutine(Foo x) {时,您的对象bar被复制(因此在函数完成后调用析构函数)。

尝试使用:void subroutine(Foo &x) {,它应该可以正常工作。

答案 3 :(得分:1)

您遇到的问题是您按价值传递了对象:

void subroutine(Foo x) {

这是创建一个临时对象,并在每次调用时调用对象的复制构造函数/析构函数。