为什么官方Qt示例和教程不使用智能指针?

时间:2015-12-23 10:21:02

标签: c++ qt c++11

为什么关于Qt库的官方示例和教程从不使用智能指针?我只看到newdelete来创建和销毁小部件。

我搜索了理由但我找不到它,我自己也没有看到它,除非它是出于历史原因或向后兼容性:不是每个人都希望程序在窗口小部件构造函数失败时终止,并通过try / catch块只是丑陋(即使在少数地方使用)。父窗口小部件可能取得孩子们的所有权这一事实也只能部分解释我的事情,因为你仍然需要在某种程度上为父母使用delete

5 个答案:

答案 0 :(得分:59)

因为Qt依赖父子模型来管理Qobject资源。它遵循复合+责任链模式,从事件管理到内存管理,绘图,文件处理等......

实际上,尝试在共享\唯一指针中使用QObject是过度工程(99%的时间)。

  1. 您必须提供一个自定义删除器,它将调用deleteLater
  2. 您父母的qobject已在父对象中有引用。所以你知道,只要父对象存在,对象就不会泄露。当您需要摆脱它时,您可以直接致电deleteLater
  3. 没有父母的QWidget已经有reference in the Qapplication object。与第2点相同。
  4. 那就是说,你仍然可以在Qt中使用RAII。例如,QPointer表现为QObject的弱引用。我会使用QPointer<QWidget>而不是QWidget*

    注意:听起来不太狂热,两个字:Qt + valgrind。

答案 1 :(得分:22)

智能指针

智能指针类std::unique_ptrstd::shared_ptr用于内存管理。拥有这样一个智能指针意味着你拥有指针。但是,在使用QObject父级创建QObject或派生类型时,所有权(清理责任)将移交给父级QObject。在这种情况下,标准库智能指针是不必要的,甚至是危险的,因为它们可能会导致双重删除。哎呀!

孤儿的原始指针

但是,当在没有父QObject的情况下在堆上创建QObject(或派生类型)时,情况就大不相同了。在这种情况下,您不应该只持有一个原始指针,而是一个智能指针,最好是对象的std::unique_ptr。这样你就获得了资源安全。如果您稍后将对象所有权移交给父QObject,则可以使用std::unique_ptr<T>::release(),如下所示:

auto obj = std::make_unique<MyObject>();
// ... do some stuff that might throw ...
QObject parentObject;
obj->setParent( &parentObject );
obj.release();

如果您在给孤儿父母之前所做的事情会引发异常,那么如果使用原始指针来保存对象,则会发生内存泄漏。但上面的代码可以防止这种泄漏。

更一般的说明

避免使用拥有原始指针不是现代C ++建议,而是避免使用原始指针。我可能会添加另一个现代C ++建议:不要为其他程序实体拥有的对象使用智能指针。

答案 2 :(得分:13)

您已经回答了自己的问题:except if it's for historic reasons/backward compatibility。像QT一样庞大的库不能假设使用该库的每个人都有支持C ++ 11的编译器。 newdelete保证在早期标准中存在。

但是,如果您确实支持使用智能指针,我会鼓励在原始指针上使用它们。

答案 3 :(得分:10)

除了@Jamey所说的:

如果巧妙地设计它,您可能永远不必在小部件上使用删除。 假设您有一个主窗口,并且您正在创建它的自动对象并在事件循环中运行该窗口。 现在,可以将此窗口小部件中的所有项目添加为其子项。 由于您将它们作为子项直接/间接地添加到此MainWindow,当您关闭此主窗口时,一切都将自动处理。 只需要确保您创建的所有动态对象/小部件都是MainWindow的子/孙。 因此无需明确删除..

答案 4 :(得分:5)

  • private Class<?>[] scanForTasks(String packageStr) { Reflections reflections = new Reflections((new ConfigurationBuilder()).setScanners(new Scanner[]{new SubTypesScanner(), new TypeAnnotationsScanner()}).setUrls(ClasspathHelper.forPackage(packageStr, new ClassLoader[0])).filterInputsBy((new FilterBuilder()).includePackage(packageStr))); Set classes = reflections.getTypesAnnotatedWith(Task.class); Class[] taskArray = (Class[])classes.toArray(new Class[classes.size()]); return taskArray; } } 定义了父级,并且树的结构类似于 程序允许非常有效地管理内存。

  • Qt中的活力打破了这个不错的理想,例如:传递一个原始指针。人们可以很容易地持有QObject,但这是编程中的常见问题。

  • Qt智能指针实际上是一个弱引用,是dangling pointer
    并提供一些STL糖果。

  • 也可以与QPointer<T>等混合,但它应该 仅用于程序中的非Qt机械。