【问题标题】:Strange behaviour in Qt program when playing with destructors使用析构函数时 Qt 程序中的奇怪行为
【发布时间】:2012-04-04 11:11:39
【问题描述】:

最近开始学习qt,一直在玩,也想起了一点c++,因为好久没用了……

不管怎样,我做了这个简单的测试类来看看当你有一个悬空指针时会发生什么:

#include <QtCore/QCoreApplication>

#include <iostream>

class Test{
public:
    QString str;
    int i;
public:
    Test(){
        i=1337;
        str="str";
        std::cout<<"initialized\n";
    }

    ~Test(){
        std::cout<<"destructor called\n";
        delete &str;
        delete &i;
        /*the program MAY run if the deletes aren't called;*/
    }
};

Test* p;

void test_function(){
    Test a;
    p=&a; //this will give a segfault, since the destructor of a is called at the end of this function's scope, but p still points to a
    //p=new Test(a); //this will call the copy constructor of p, copying the values from a to p
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    test_function();

    std::cout<<p->str.toStdString() << p->i<<std::endl;

    return a.exec();
}

果然,它崩溃了(但它没有说明原因 - 可能是段错误)。但是,如果我注释了调用悬空指针的行(main() 中的 cout),程序现在会崩溃,给我一个“std::bad_alloc”异常。以下独立测试的事物列表导致不再抛出异常:

  • 注释str的初始化;
  • 评论str的析构函数;
  • 用 std::string 替换 QString;

我尝试过但没有任何效果的是用 QString::fromStdString("str") 初始化 str(而不仅仅是 "str")。

我还尝试使用 try/catch 块围绕 str(同时是 QString)的初始化和销毁​​,但无济于事。此外,尝试/捕获 test_function() - 没有结果。然而,我确实通过在 try/catch 块中包围 a.exec() 成功地捕获了异常。

为什么当 cout 被注释时它会抛出异常,但否则只会崩溃? 为什么同时拥有 str 的初始化器和析构器会引发异常,但评论一个或两个只会导致崩溃? (现在我正在写这篇文章,我意识到这可能是唯一的问题;但是,为什么它会崩溃 QString 而不是 std::string?) 为什么 a.exec() 会抛出异常?

比我更了解 qt(甚至 c++)的人能否解释一下发生了什么? 谢谢

【问题讨论】:

  • 不要想太多。通常,如果您发现自己在编写析构函数,则说明您做错了。
  • 您将通过清理您不打算犯的错误并重试来了解更多信息。我仍然无法准确说出您要测试的内容。
  • @tmpearce:好吧,我试图在一个简单的程序中测试析构函数,看看它们何时被调用,删除是否真的删除了数据等;不知何故,我在 main() 中结束了对 cout 的评论,我得到了一个异常而不是崩溃。从那里开始,我开始使用它,看看是什么导致了异常,什么没有......
  • 我明白,我不是在批评 - 试图理解你的代码是一件好事 - 但试图理解“未定义的行为”是愚蠢的差事。我建议阅读stackoverflow.com/q/2397984/1214731
  • 谢谢!一本好书,我想我以前还没有真正偶然发现过这些东西。很高兴知道:)

标签: c++ qt segmentation-fault destructor


【解决方案1】:

这是一个错误:

delete &str;
delete &i;

尝试delete 未动态分配的变量,即使用new。删除这两行。 istr 会在Test 实例被销毁时自动销毁。

【讨论】:

  • 我认为这可能是问题所在,但为什么 std::string 不会崩溃?另外,为什么不建议删除未分配的变量?它们会自动销毁吗?
  • 我很确定这是未定义行为的领域:意味着任何事情都可能发生。
  • 是的,它们会自动销毁。将deletenewdelete[]new[] 一起使用。
  • 动态分配的变量是在运行时创建的,而非动态分配的变量是在编译时创建的,并在程序中内置了清理代码
  • @NiniMichaels,这是未定义的行为:任何事情都可能发生。
【解决方案2】:

我不确定您正在寻找什么样的答案,但即使您不使用悬空指针p,您也会损坏堆(或其他一些未定义的行为),因为在您的Test 的 dtor 你有以下内容:

~Test(){
    std::cout<<"destructor called\n";
    delete &str;
    delete &i;
    /*the program MAY run if the deletes aren't called;*/
}

两个delete 语句正在删除不是通过new 分配的东西——不要那样做。

【讨论】:

  • 感谢您的回答!我也问过hmjd:为什么不推荐这个?当你删除一个未分配的对象时会发生什么?为什么会发生在 QString 而不是 std::string?
  • 如果你删除堆栈分配的变量,你通常会使程序崩溃——除非你不走运,否则你的程序会在别处搞砸。阅读本文了解更多信息:stackoverflow.com/questions/441831/…
  • 删除不是通过new 分配的对象类似于在不是通过malloc() 分配的指针上调用free()。您可能永远不会注意到问题,但您可能会立即或在将来的分配/解除分配期间破坏事物并导致崩溃。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-22
  • 1970-01-01
相关资源
最近更新 更多