【问题标题】:how to release QGLWidget from QGraphicsView如何从 QGraphicsView 释放 QGLWidget
【发布时间】:2017-02-06 14:08:11
【问题描述】:

我看到许多关于使用 OpenGL 进行图形视图的 Qt 示例。 大多数示例如下所示:

MyGraphicsView::MyGraphicsView(......)
{
gl = new QGLWidget();
this->setViewport(gl);
....

其中 QGLWidget* gl 是 MyGraphicsView 的成员。

我的问题是在那之后我应该如何删除“gl”。 我的类继承自 QGraphicsView 会自动删除它吗? 在 MyGraphicsView 析构函数中手动删除“gl”会导致崩溃。

在 Qt 中真的很难理解哪个对象会被自动删除,哪个不会。

【问题讨论】:

    标签: qt destructor qgraphicsview qglwidget


    【解决方案1】:

    在 Qt 中真的很难理解哪个对象会被自动删除,哪个不会。

    一点也不。你只需要知道两件事:

    1. C++ 范围的语义。但你已经知道这些了——希望如此。

    2. QObject::~QObject() 删除其幸存的子代:

      QObject::~QObject() {
        ...
        for (auto child : children())
          delete child;
        ...
      }
      

      显然,如果一个子节点进入~QObject() 之前已经被销毁,那么它就不会出现在父节点的children() 列表中,也不会被双重删除。不知何故,这似乎让大多数人望而却步,但如果人们不认为 Qt 具有某种魔力,它不应该。 Qt 做了 C++ 允许它做的事情。不多也不少。

    因此,如果其中一个为真,则将为您销毁一个对象:

    1. 你在一个范围内按值持有它,例如

      class MyGraphicsView : public QGraphicsView {
         QOpenGLWidget m_gl;
      public:
         MyGraphicsView(QWidget * parent = nullptr) : QGraphicsView{parent} {
            setViewport(&m_gl); // sets m_gl's parent
            Q_ASSERT(m_gl.parent());
         }
      };
      

      m_gl 成员将在~MyGraphicsView 的主体返回后被销毁。破坏顺序如下:

      this->~MyGraphicsView()
      m_gl.~QOpenGlWidget()  // the viewport widget ceases to exist here
      this->~QGraphicsView()
      this->~QAbstractScrollArea()
      this->~QFrame()
      this->~QWidget()
      this->~QObject()  // has no children to delete
      
    2. parent 的 ~QObject() 析构函数执行时,它仍然存在并且有一个父级。回想一下,所有小部件都是QObjects。

      在上面的示例中,m_gl 对象将在 MyGraphicsView::~QObject 运行之前被销毁 - 并因此不复存在,因此不存在双重销毁的可能性。

      但您也可以采用过早悲观的指针保持方法:

      // Don't code like this. It's silly.
      class MyGraphicsView : public QGraphicsView {
         QOpenGLWidget * m_gl;
      public:
         MyGraphicsView(QWidget * parent = nullptr) :
            QGraphicsView{parent},
            m_gl{new QOpenGLWidget}
         {
            setViewport(m_gl); // sets m_gl's parent
            Q_ASSERT(m_gl->parent());
         }
         ~MyGraphicsView() {
            Q_ASSERT(m_gl->parent()); // make sure ~QObject will delete the child
         }
      };
      

      以下是破坏顺序:

      this->~MyGraphicsView()
      m_gl.~pointer       // a trivial destructor of a pointer value
      this->~QGraphicsView()
      this->~QAbstractScrollArea()
      this->~QFrame()
      this->~QWidget()
      this->~QObject()
        for (auto child : children())
          delete child
            ~QOpenGlWidget()  // m_gl is long gone at this point!
      

      由于m_gl 成员的销毁是微不足道的,并且不会对指针本身指向的内容做任何事情,因此QOpenGlWidget 实例将持续存在直到QObject 析构函数运行,此时它会迭代其子列表并deletes 每个孩子 - 从而删除 QOpenGlWidget 实例。

    在 MyGraphicsView 析构函数中手动删除“gl”会导致崩溃。

    不,它不会,除非你没有告诉我们什么。以下在 Qt 4 和 Qt 5 中都可以正常工作。手动删除是无害的,尽管完全没有必要:

    // https://github.com/KubaO/stackoverflown/tree/master/questions/opengl-viewport-val-39750134
    #include <QtGui>
    #include <QtOpenGL>
    #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
    #include <QtWidgets>
    #else
    using QOpenGLWidget = QGLWidget;
    #endif
    
    // Don't code like this. It's silly.
    class MyGraphicsView : public QGraphicsView {
       QOpenGLWidget * m_gl = new QOpenGLWidget;
    public:
       MyGraphicsView(QWidget * parent = nullptr) : QGraphicsView{parent}
       {
          setViewport(m_gl); // sets m_gl's parent
          Q_ASSERT(m_gl->parent());
       }
       ~MyGraphicsView() {
          delete m_gl; // completely unnecessary
       }
    };
    
    int main(int argc, char ** argv) {
       QApplication app{argc, argv};
       MyGraphicsView view;
       QGraphicsScene scene;
       scene.addText("Hello World");
       view.setScene(&scene);
       view.show();
       return app.exec();
    }
    

    【讨论】:

      猜你喜欢
      • 2011-02-16
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-12
      • 1970-01-01
      • 2019-09-10
      相关资源
      最近更新 更多