【问题标题】:Does this code provide memory leaks?此代码是否提供内存泄漏?
【发布时间】:2016-02-04 03:20:34
【问题描述】:

最后我安装了 Ubuntu 并设置了 Qt+Valgrind 以防止我在 Windows 中无法做到的内存泄漏。所以我不明白这段代码是否提供内存泄漏?事实上,Valgrind 说我只有 500 多个问题,但对泄漏一无所知。我

#include <QWidget>
#include <QFrame>
#include <QVBoxLayout>
#include <QApplication>

int main(int argc, char *argv[])

{
    QApplication a(argc, argv);

    QWidget * wdgt = new QWidget;  //this line should be the cause of leakage 
                                   //if it exist (as far as i know)
    QVBoxLayout *layout = new QVBoxLayout;
    QFrame * frame = new QFrame;

    frame->setFrameStyle(QFrame::Panel | QFrame::Plain);
    frame->setLineWidth(5);
    layout->addWidget(frame);

    wdgt->setLayout(layout);
    wdgt->setFixedSize(800,600);
    wdgt->show();

    return a.exec();
}

【问题讨论】:

  • 不,因为操作系统会处理它。
  • 但在这种情况下它不是int * a = new int;
  • 每个new 都应该有对应的delete。或者使用智能指针
  • @EdHeal 在 Qt 的情况下不是,如果对象有父对象,则父对象会销毁它

标签: c++ qt memory-leaks valgrind


【解决方案1】:

看到这个帖子:Creating and deallocating a Qt widget object

它解释了如果一个Qt对象有一个父对象,当父对象被销毁时,它会被自动删除。

在您的代码中:

  • wdgtlayout 的父级,因为你做了 wdgt-&gt;setLayout(layout)
  • wdgtframe 的父级,因为您执行了 layout-&gt;addWidget(frame) 并且 layout 的父级是 wdgt。正如 thuga 所说,布局将所有权传递给他们自己的父级。

在您的代码中,只有 wdgt 是孤立的(没有 Qt 父级自动删除它)。

要解决此问题,您可以给他一个父母:

QWidget * wdgt = new QWidget(&app);

这样wdgtapp 的孩子,然后在app 被销毁时会自动删除。

或自行删除:

int main(int argc, char *argv[])
{
    ...
    int res = a.exec();
    delete wdgt; // this will delete wdgt, but also frame and layout
    return res;
}

或者,最后,将它创建为一个对象,以便在超出范围时将其删除:

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

    QWidget wdgt;

    QVBoxLayout *layout = new QVBoxLayout;
    QFrame * frame = new QFrame;

    frame->setFrameStyle(QFrame::Panel | QFrame::Plain);
    frame->setLineWidth(5);
    layout->addWidget(frame);

    wdgt.setLayout(layout);
    wdgt.setFixedSize(800,600);
    wdgt.show();

    return a.exec();
}

顺便提一下,如果你做QVBoxLayout *layout = new QVBoxLayout(wdgt),就不需要做wdgt-&gt;setLayout(layout)。所以这两段代码是等价的:

QVBoxLayout *layout = new QVBoxLayout(wdgt); // parenting upon construction

等同于:

QVBoxLayout *layout = new QVBoxLayout; // no parent
wdgt->setLayout( layout ); // reparenting

【讨论】:

  • 我认为最干净的方法是在堆栈上创建 QWidget wdgt
  • 实际上layoutframe 的父级都是wdgt。布局不获取小部件的所有权,它们只是将所有权传递给它们自己的父级。
  • 我认为澄清这一点可能很重要,因为我已经看到人们删除布局,假设它将摆脱这些布局中的小部件。这是因为他们认为那些widget的父级就是布局,破坏父级就会破坏它的子级。
【解决方案2】:

是的,您的代码会泄漏内存,因为您使用 new 创建对象而不使用 Qt 的内存管理。

QApplication a(argc, argv);
QWidget * wdgt = new QWidget(&app);
QVBoxLayout *layout = new QVBoxLayout(wdgt); // optional, setLayout does that
QFrame * frame = new QFrame(layout); // optional, addWidget does that

使用 Qt 的内存管理。


您也可以使用 C++11 共享指针:

QApplication a(argc, argv);
std::shared_ptr<QWidget> wdgt = std::make_shared<QWidget>();

QVBoxLayout *layout = new QVBoxLayout;
QFrame * frame = new QFrame;

一旦共享指针的最后一个用户超出范围,您的对象就会被自动删除。

【讨论】:

  • 只需要QWidget(&amp;app)。其他项目是父项(QWidget::setLayout 使QWidget 成为QLayout 的父项,QLayout::addWidget 使QLayout 成为QWidget 的父项)。只有 wdgt 是 OP 中的孤儿。
  • 当你将它们添加到布局和布局时,小部件也会自动重新设置父级,等等
  • 而且在这种情况下,共享的ptrs也会导致双重删除(因为reparenting)
  • 谢谢大家!它对我帮助很大。
  • 我肯定会避免将 shared_ptr 与 Qt 对象混合。这是两种不同的“自动删除”机制,您很可能最终会出现双重删除...
【解决方案3】:

您的代码会泄漏内存,但首先您不应该编写甚至需要担心资源泄漏的代码。让编译器为你处理:

// main.cpp
#include <QtWidgets>

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    QWidget widget;
    QVBoxLayout layout(&widget);
    QFrame frame;

    frame.setFrameStyle(QFrame::Panel | QFrame::Plain);
    frame.setLineWidth(5);
    layout.addWidget(&frame);

    widget.setFixedSize(800,600);
    widget.show();
    return a.exec();
}

【讨论】:

  • 不能同意你的看法
  • 我不确定,但上面的 cmets 假设当指针被传递时,它们被拥有,因此将被删除。如果它们在堆栈上,这是不可能的。请澄清
  • @EdHeal 这也有效,只要对象以正确的顺序创建。见Object trees & ownership
  • @EdHeal 指针被传递,它们被拥有,因此将被删除 QObject 没有对其子项的无限记忆。当一个孩子被破坏时,它的父母显然不会试图删除它。当涉及到值的破坏顺序时,C++ 具有非常有用的语义。我很惊讶人们编写 C++ 代码却没有意识到这个顺序是有充分理由的,并且程序员应该利用它来为她谋福利。
  • @Mikhail 为什么?它对任何事情都没有帮助。现代 C++ 读起来应该更像 Python,而不是 C。如果你到处都看到手动内存管理,那么你做的事情非常非常错误。
猜你喜欢
  • 1970-01-01
  • 2017-12-05
  • 1970-01-01
  • 1970-01-01
  • 2020-01-29
  • 1970-01-01
  • 2012-09-27
  • 2013-04-04
  • 1970-01-01
相关资源
最近更新 更多