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

Plo*_*log 6 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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢.

jua*_*nza 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
}
Run Code Online (Sandbox Code Playgroud)

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

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


小智 6

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

void subroutine(Foo& x)
{
    cout << "Hello!" << endl;
}
Run Code Online (Sandbox Code Playgroud)

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

class Foo {
private:

    double *array;

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

public:
    ...
};
Run Code Online (Sandbox Code Playgroud)

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