I need some explanation about the next situation. Assume we have the next code:
class MyClass : public QObject
{
public:
MyClass(QObject* parent = nullptr)
{
m_member.reset(new QObject(this));
}
~MyClass(){} override;
private:
QScopedPointer< QObject> m_member;
};
I can't understand if it's safe to pass an object with a parent to QScopedPointer. Might it be any situation when the object is deleted twice by the parent and by the smart pointer and it will lead to a crash?
This is completely safe. Here is what happens when an instance of MyClass gets destroyed:
MyClass's destructor gets called (which does nothing in your example)
all member variables of MyClass get destructed. In your case, QScopedPointer's destructor gets called, which means that the child QObject is deleted. When a QObject is destroyed it is removed from its parent's list of children, so the parent no longer tries to delete this QObject
QObject's destructor for your MyClass instance gets called and it sees no children to delete
You may also want to consider holding your child member QObject by value if there is no reason to allocate it dynamically
Related
I have the following classes:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QStringList pluginsToStart, QWidget *parent = 0);
~MainWindow();
// some other stuff
public slots:
void on_timeDataChanged(logging::TimeValueVector<bool>& aData);
void on_importStarted();
}
and
class DataImporterWidget : public PluginWidget
{
Q_OBJECT
public:
explicit DataImporterWidget(QWidget *parent = 0);
~DataImporterWidget();
void initConnections(QMap<QString, PluginWidget*> pluginWidgetMap);
in the method initConnections, I want the widget to init the signal-slot connections like so:
void DataImporterWidget::initConnections(QMap<QString, PluginWidget*> pluginWidgetMap)
{
for(Importer* importer : this->getImporterMap().values())
{
connect(importer, SIGNAL(signal_timeDataChanged(logging::TimeValueVector<bool>&)),
parentWidget(), SLOT(on_timeDataChanged(logging::TimeValueVector<bool>&)));
}
connect(this, SIGNAL(signal_importStarted()), parentWidget(), SLOT(on_importStarted()));
}
Importer is a QGroupBox and a base class for derived sub classes specifying concrete data importer types.
It works like so: If I press a button, an DataImporterWidget is created and added to a QMdiArea as a QMdiSubWindow. When creating the DataImporterWidget I call the initConnections() method which sets up the signal-slot connections.
Now, when I run the program, I get the following message:
QObject::connect: No such slot
QMdiSubWindow::on_timeDataChanged(logging::TimeValueVector<bool>&) in src/dataimporter/DataImporterWidget.cpp:81
QObject::connect: No such slot QMdiSubWindow::on_importStarted() in src/dataimporter/DataImporterWidget.cpp:85
QObject::connect: (sender name: 'DataImporterWidget')
I do not understand why I get it because the slot is there. Even if I cast the parentWidget to the MainWindow, I get the same error.
PluginWidget is just a base class deriving from QWidget that holds some common functionality for my used plugins.
I put Q_OBJECT on each base and derived class but still get this error. However, if I set up the connections in the MainWindow, it works just fine, but I wonder why the above solution won't work.
Don't create the connection from child object, instead create it from parent object code after creating the child object.
This way you won't need to cast any type.
You did not shown a huge chunk of important code (like creating DataImporterWidget, setting MainWindow as its parent, the place where you call initConnections...). However, you said
If I use the new signal slot syntax, my program crashes with a
segmentation fault...
If it crashes, than you have to find a reason why. Using old signal-slot connect syntax does not cure the disease, it just delay its manifestation. According to this, the reason why you get a segfault can be parentWidget() == nullptr or parent is not initialized yet.
My advice, check your code, and make user the parent of DataImporterWidget is created and specified before your call initConnections().
I've found the problem. The reason is, that the MainWidget class holds a QMdiArea where I add my PluginWidgets. So, when I create the PluginWidget, I set the MainWidget as its parent, but as soon as I add it to the QMdiArea, it also becomes a child of QMdiSubWindow. The parentWidget was never null but it was the wrong one ...
Let's say I have a C++ object with a member object that I expose to QML:
class X : public QObject
{
Q_OBJECT
};
class Y : public QObject
{
Q_OBJECT
Q_PROPERTY(X* x READ getX CONSTANT)
public:
X* getX(void) { return &x; }
X x;
};
Most of the time this works, but sometimes it will cause crashes. The call stack is quite lengthy but goes like this:
QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData> >::data
qGetPtrHelper<QScopedPointer<QObjectData>>
QObject::d_func
QObjectPrivate::get
QQmlNotifierEndpoint::disconnect
QQmlNotifierEndpoint::~QQmlNotifierEndpoint
QQmlJavaScriptExpressionGuard::~QQmlJavaScriptExpressionGuard
QRecyclePool<QQmlJavaScriptExpressionGuard, 1024>::Delete
QQmlJavaScriptExpressionGuard::Delete
QQmlJavaScriptExpression::GuardCapture::captureProperty
QQmlEnginePrivate::captureProperty
QV4::QObjectWrapper::getProperty
etc.
If instead I set X to be a pointer:
class Y : public QObject
{
Q_OBJECT
Q_PROPERTY(X* x READ getX CONSTANT)
public:
Y()
{ x = new X; }
X* getX(void) { return x; }
X* x;
};
The crashes go away.
Is this a known restriction of Q_PROPERTY, that if you return a pointer to a QObject, that object is assumed to be a pointer (not a member) and things like deleteLater() might be called?
This crash most likely happens because QML takes ownership of the object c your function returns, and want to delete it at some point late.
In your first example, since x is not allocated from the free store, trying to delete it will crash.
In the second example, the QML engine still tries to delete it, but it does so without problem.
The question to ask is why does the QML engine takes ownership of the object ?
The documentation states in Data Type Conversion Between QML and C++ | Data Ownership :
When data is transferred from C++ to QML, the ownership of the data always remains with C++. The exception to this rule is when a QObject is returned from an explicit C++ method call: in this case, the QML engine assumes ownership of the object, unless the ownership of the object has explicitly been set to remain with C++ by invoking QQmlEngine::setObjectOwnership() with QQmlEngine::CppOwnership specified.
Additionally, the QML engine respects the normal QObject parent ownership semantics of Qt C++ objects, and will not ever take ownership of a QObject instance which already has a parent.
So, in order to remedy to your problem, you could either affect a QObject parent to your x object, or explicitely declaring it having QmlEngine::CppOwnership.
I use observer-observable pattern in my program. Everything worked before I had to change the code a little. If to be exact I changed the inheritance of IObserver class - right now it inherits QObject:
class IObserver : public QObject
{
...
I did it because of only one thing - I need deleteLater() method to be used in an observer, so I would be able to call implementation of virtual function deinitialization() of IObserver. Thus I could standardize every IObserver message handler.
The problem is, I already inherited QObject (indirectly) in some Observer classes. Like MainForm or AboutDialog. Everything is going fine until I try to call "connect" method in AboutDialog class.
What can I do? I really need this deleteLater() method since I can't use "delete this" in IObserver code - this will call IObserver destructor, not the MainForm or Storage classes for instance.
Thank you.
I really need this deleteLater() method since I can't use "delete this" in IObserver code - this will call IObserver destructor, not the MainForm or Storage classes for instance.
If you make your destructor virtual (and you should!) it will call the derived destructor just fine. But a problem is that destructing a object while it is handling some signal/slot might cause problems with the event loop. You would have to be very careful with delete this anyway.
The problem is, I already inherited QObject (indirectly) in some Observer classes.
One way you could implement this, not sure if the best thought:
template <typename Derived>
class IObserver
{
// Just to be sure: (C++11)
static_assert(is_base_of<Derived, QObject>::value,
"must inherit from QObject when using IObserver");
void deleteMe()
{
QObject* thisObject = dynamic_cast<QObject*>(this);
// no need for check if thisObject equals null. static assert does this.
thisObject->deleteLater();
}
};
class MainForm : public IObserver<MainForm>, public QMainWindow
{
// ...
};
I believe this pattern is called static polymorphism.
Abandon inheritance of QObject for IObserver. Instead of that add such method to interface.
class IObserver : public QObject {
public:
QObject *object() const = 0;
...
Then if implementation of interface inherits the QObject you will return this pointer from object() method. If implementation of interface doesn't inherit QObject you can simply return pointer to some simple QObject which will handle destruction of this object.
Then you can simply connect deleteLater for object returned by this method.
Off topic
Use of interfaces for observing in Qt usually is obsolete, slots and signals do this job perfectly and this is more flexible approach.
I have a class called MainWindow and it initializes objects of GLWidget and clothWidget class, both aforementioned classes inherit QGLWidget class. Initialization in MainWindow class is like
glWidget = new GLWidget();
clWidgetf = new clothWidget();
and I have an object of GLWidget class declared in clothwidget.h. When I do
clwidgetf->gl = glwidget ( here gl is object of class GLWidget declared in ClothWidget class) in MainWindow
I get following errors
/usr/include/qt4/QtOpenGL/qgl.h:592: error: 'QGLWidget&
QGLWidget::operator=(const QGLWidget&)' is private
/home/arun/Desktop/garment/glwidget.h:8: error: within this context
The problem is you are (unintentionally) trying to copy the widget, and since it is QObject based it cannot be copied. In the "olden days" (until very recently) the only way to make that was to make the copy constructor private, which is exactly the error message you are getting. Double-check your code to make sure you are not passing as a value copy (as Riateche suggested in the comments).
If you want a reference then you have to initialize it with your object, you can not set it elsewhere, but most likely you want a pointer so you should pass pointer to your function and make your class member a pointer too, or take an adress of passet reference and still assign it to some pointer.
And you can't copy anything derived from QObject since constructors are private.
As we all know, Q_OBJECTs are instances and are not copyable.
Is there any kind of syntactic sugar to copy all static and dynamic properties of an arbitrary QObject derived class?
It seems such a nobrainer, but I can't find any reference to such - obviously implementing one myself should be quite trivial - loop over metaObject(), loop over dynamicPropertyNames(), set accordingly.
You could implement a copy helper class as follows.
/** Enable QObjects to be explicitly copyable by copying property values. */
template<class T>
class QObjectCopyHelper<T>
{
protected:
explicit QObjectCopyHelper(T *client) : m_client(client) {}
public:
T *clone(QObject *parent = 0) {
T *copy = new T(parent);
// loop over and copy properties from m_client to copy
// (both from T::staticMetaObject and dynamic ones)
return copy;
}
private:
T *m_client; // <-- I think we need this, but I might be wrong
};
Then you can use this in any QObject subclass with very low work needed to be done:
class MyClass : public QObject, public QObjectCopyHelper<MyClass>
{
Q_OBJECT
...
};
However, this still needs clone() to be called (the ugly "Java-style"). So we can additionally define a copy constructor just calling clone() and you also might think of a assign() method called within the assignment operator.
Please note that this really copies the properties only! There are a lot of other things being tracked in QObject, like the current connections. They explicitly forbid copying QObjects because it would be very difficult to define rules on how this should be done, and these rules would be the correct ones for some use cases only, while in others you want other rules...
A solution is discussed here, where they take the loop-over-the-properties approach. There seems to be no "syntactic sugar" here.