我看到许多关于使用OpenGL进行图形视图的Qt示例。大多数示例看起来像这样:
MyGraphicsView::MyGraphicsView(......)
{
gl = new QGLWidget();
this->setViewport(gl);
....
where QGLWidget* gl是MyGraphicsView的成员
我的问题是如何之后,我应该删除"gl"。它会被自动删除与我的类继承从QGraphicsView?手动删除MyGraphicsView解构器中的"gl"会导致崩溃。
在Qt中真的很难理解哪些对象会被自动删除,哪些不会。
在Qt中真的很难理解哪些对象会被自动删除,哪些不会。
一点也不。你只需要知道两件事:
-
c++作用域的语义。但是你已经知道这些了——希望如此。
-
QObject::~QObject()
删除其存活的子代:QObject::~QObject() { ... for (auto child : children()) delete child; ... }
显然,如果子节点在进入
~QObject()
之前已经被销毁了,那么它就不会出现在父节点的children()
列表中,也不会被双重删除。这在某种程度上似乎让大多数人感到困惑,但如果你不认为Qt是一种魔法,那么不应该。Qt做c++允许它做的事情。不多不少。
因此,如果其中一个为真,对象将被析构:
-
在作用域中按值保存,例如
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
-
它仍然存在,并且在父类
~QObject()
析构函数执行时已经有了父类。回想一下,所有的小部件都是QObject
s。在上面的例子中,
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
析构函数运行,此时它迭代其子列表,delete
迭代每个子列表,从而删除QOpenGlWidget
实例。
不,没有,除非你有事瞒着我们。下面的代码在Qt 4和Qt 5中都可以正常工作。手动删除是无害的,尽管完全没有必要:手动删除MyGraphicsView解构器中的"gl"会导致崩溃。
// 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();
}