6

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

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

#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

4 回答 4

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对象时,每个副本都引用同一个数组。每个副本都会试图摧毁它。

您应该遵循三个规则并实现一个赋值运算符和一个复制构造函数,它们对数组进行“深度复制”,这样每个Foo对象都拥有自己的数组。

于 2012-10-05T13:15:23.110 回答
6

您正在将 bar 按值传递给子例程,因此正在创建一个副本。为了避免复制通过引用传递它:

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

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

class Foo {
private:

    double *array;

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

public:
    ...
};

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

于 2012-10-05T13:17:12.920 回答
3

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

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

于 2012-10-05T13:16:07.777 回答
1

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

void subroutine(Foo x) {

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

于 2012-10-05T13:16:03.940 回答