析构函数被提前调用

时间:2013-05-23 14:54:03

标签: c++

我有一个队列类,其中我正在尝试动态分配3个“示例”对象并将它们放入队列,然后出列并删除它们。但是样本对象的析构函数:

~Sample() { cout << "Destructing Sample object " << id << "\n"; }

当我尝试将对象放在队列中时,由于某种原因被调用。

int main()
{
   Sample *samp1;
   Sample *samp2;
   Sample *samp3;
   Queue<Sample> sQa(3);
   samp1 = new Sample(1);
   samp2 = new Sample(2);
   samp3 = new Sample(3);

   cout << "Adding 3 Sample objects to queue...\n";

   sQa.put(*samp1);
   sQa.put(*samp2);
   sQa.put(*samp3);

   cout << "Dequeing and destroying the objects...\n";

   for(int i = 0; i < 3; ++i)
   {
       delete &sQa.get();
   }

   return 0;
}

这是输出:

Adding 3 sample objects to queue...
Destructing Sample object 1
Destructing Sample object 2
Destructing Sample object 3
Dequeing and destroying the objects...
Destructing Sample object 1

然后程序崩溃了。谁知道为什么会这样?此外,这是必要的put函数。 (队列类是模板)

void put(Qtype data) 
{
    if(putloc == size) 
    {
        cout << " -- Queue is full.\n";
        return;
    }

    putloc++;
    q[putloc] = data;   
}

4 个答案:

答案 0 :(得分:10)

您的代码会导致未定义的行为。它在未使用delete动态分配的对象上调用new

使用:

sQa.put(*samp1);

您可以在sQa中存储动态分配的成员的副本。按值复制对象后,丢失了指向动态分配对象的指针。

解决方案是使用智能指针作为容器元素,让RAII为您完成所有内存管理工作。

答案 1 :(得分:3)

问题的根源

delete &sQa.get();

您正在new删除与分配的对象无关的内容。

正确的方法如下:

Queue<Sample*> sQa(3);
samp1 = new Sample(1);
samp2 = new Sample(2);
samp3 = new Sample(3);

sQa.put(samp1);
sQa.put(samp2);
sQa.put(samp3);

cout << "Dequeing and destroying the objects...\n";

for(int i = 0; i < 3; ++i)
{
   delete sQa.get();
}

注意:最好使用unique_ptrshared_ptr之类的智能指针代替裸指针。

答案 2 :(得分:1)

除非Qtype是typedef,否则put()函数指定其参数应该通过副本传递。无论何时调用它,它都会调用Sample的复制构造函数来创建一个临时对象。

答案 3 :(得分:1)

你编码分配对象;将它们复制到队列中;从队列中检索副本;并尝试删除这些副本。原始对象被泄露(因为您没有delete samp1等),并且最终对象无法删除,因为它们不是使用new创建的。

您看到的析构函数来自在将已分配的对象复制到队列时创建的临时对象;崩溃是从删除错误对象中获得的未定义行为。

由于队列包含对象而不是指针,因此根本不需要动态分配:

cout << "Adding 3 Sample objects to queue...\n";

sQa.put(Sample(1));
sQa.put(Sample(2));
sQa.put(Sample(3));

cout << "Dequeing and destroying the objects...\n";

for(int i = 0; i < 3; ++i)
{
   sQa.get();
}

如果你想试验动态分配,那么你应该修改队列来存储指针;但请记住,在没有RAII帮助的情况下管理动态对象非常困难,所以不要在任何需要可靠的代码中尝试这样做。