C ++:在超出范围之前调用析构函数?
Posted
技术标签:
【中文标题】C ++:在超出范围之前调用析构函数?【英文标题】:C++: Destructor being called before it should go out of scope? 【发布时间】:2012-09-26 15:45:17 【问题描述】:我在子例程结束时为一个类调用析构函数时遇到问题,即使它应该在子例程范围之外定义。
这是显示我的问题的最小代码:
#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() 函数?这意味着当我调用第二个子例程时,析构函数再次被调用,我得到了内存泄漏。
我发现我可以通过在子例程中通过引用调用来解决这个问题,但我对这个修复不是很满意,因为我不明白为什么它一开始就不起作用。 谁能帮我解释一下?
谢谢。
【问题讨论】:
给定你的析构函数,你需要定义或删除Foo
的拷贝构造函数和拷贝赋值运算符。搜索“三法则”。
“为一个类调用析构函数” - 你会发现,随着时间的推移,如果你一直区分 class 和 object。析构函数是在对象而不是类上调用的。
【参考方案1】:
您正在将 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
对象都拥有自己的数组。
【讨论】:
谢谢您的快速回复!是的,我注意到通过引用调用修复了它,但我不知道为什么,感谢您的解释! @user1722882:实际上,您最好使用std::vector
并完全删除您的析构函数。重新实现标准库容器可能有利于它的运动,但如果它不是练习/工作的主要目的......它只会扼杀你的生产力。【参考方案2】:
您正在将 bar 按值传递给子例程,因此正在创建一个副本。为避免复制通过引用传递:
void subroutine(Foo& x)
cout << "Hello!" << endl;
您可以通过像这样声明复制构造函数和复制赋值运算符私有来防止意外复制您的类:
class Foo
private:
double *array;
Foo(const Foo&);
Foo& operator=(const foo&);
public:
...
;
然后你会得到一个编译错误。如果您确实需要能够复制您的课程,那么您实际上需要实现这些功能来执行“深度复制”(或者更好的是使用std::vector<float>
并让它为您管理内存,包括安全复制) .
【讨论】:
对于 C++11,最好将它们声明为deleted
【参考方案3】:
当您调用 void subroutine(Foo x)
时,您的对象 bar
被复制(因此在函数完成后调用析构函数)。
尝试使用:void subroutine(Foo &x)
,它应该可以正常工作。
【讨论】:
【参考方案4】:您遇到的问题是您按值传递对象:
void subroutine(Foo x)
这是创建一个临时对象并在每次调用时调用对象的复制构造函数/析构函数。
【讨论】:
以上是关于C ++:在超出范围之前调用析构函数?的主要内容,如果未能解决你的问题,请参考以下文章