Copy Constructor of Derived Qt Class

copy constructor of derived QT class

QObject Class description page tells :

QObject has neither a copy constructor nor an assignment operator. This is by design. Actually, they are declared, but in a private section with the macro Q_DISABLE_COPY(). In fact, all Qt classes derived from QObject (direct or indirect) use this macro to declare their copy constructor and assignment operator to be private. The reasoning is found in the discussion on Identity vs Value on the Qt Object Model page.

That means you are not supposed to copy QT objects, since QObject is non-copyable by design.

The first warning tells you to initialize the base class (which is QWidget). If you want to do this, you are going to construct a new base object, and I doubt that is what you want to do.

The second error is telling you what I wrote above : do not copy qt objects.

Could I have copy constructor for subclass of QObject?

Everything is true:

1. QObject can't be copied and all its descendants can't be copied also.

2. Q_DECLARE_METATYPE accepts objects with public constructor, copy constructor and destructor.

There is no contradiction, because you can't register QObject descendants with Q_DECLARE_METATYPE.

EDIT:

When you convert your class to QVariant it uses a copy constructor to make a copy of your object:

 void *ptr = QMetaType::construct(x->type, copy);

Would a derived class ever have an implicit copy constructor or assignment operator when it's deleted in the base class?

Prior to commit a2b38f6, QT_DISABLE_COPY was instead defined like this (credit to Swift - Friday Pie for pointing this out in a comment):

#define Q_DISABLE_COPY(Class) \
Class(const Class &) Q_DECL_EQ_DELETE;\
Class &operator=(const Class &) Q_DECL_EQ_DELETE;

And Q_DECL_EQ_DELETE like this:

#ifdef Q_COMPILER_DELETE_MEMBERS
# define Q_DECL_EQ_DELETE = delete
#else
# define Q_DECL_EQ_DELETE
#endif

Q_COMPILER_DELETE_MEMBERS got defined if C++11 support (or at least a new enough draft of it to support = delete) was available.

Thus, if you compiled Qt back then against a C++03 compiler, it would instead have compiled something like this:

struct Base {
Base() {};

private:
Base(const Base &);
Base &operator=(const Base &);
};

struct Derived : Base {};

int main() {
Derived d1;
Derived d2(d1);
Derived d3;
d3 = d1;
}

And compiling that with g++ -std=c++03 gives you these errors:

<source>: In copy constructor 'Derived::Derived(const Derived&)':
<source>:9:8: error: 'Base::Base(const Base&)' is private within this context
9 | struct Derived : Base {};
| ^~~~~~~
<source>:5:5: note: declared private here
5 | Base(const Base &);
| ^~~~
<source>: In function 'int main()':
<source>:13:18: note: synthesized method 'Derived::Derived(const Derived&)' first required here
13 | Derived d2(d1);
| ^
<source>: In member function 'Derived& Derived::operator=(const Derived&)':
<source>:9:8: error: 'Base& Base::operator=(const Base&)' is private within this context
9 | struct Derived : Base {};
| ^~~~~~~
<source>:6:11: note: declared private here
6 | Base &operator=(const Base &);
| ^~~~~~~~
<source>: In function 'int main()':
<source>:15:10: note: synthesized method 'Derived& Derived::operator=(const Derived&)' first required here
15 | d3 = d1;
| ^~

So back then, "your compiler will thoughtfully create it for you" was technically true but not practically so, since the compiler creating it would cause compilation to fail, just with a different (and arguably less clear) error. I'm now convinced that it's not true at all anymore now that = delete is unconditionally used, so I plan to ask Qt's maintainers to remove/reword that section of their documentation.

Why does defining a copy constructor for derived class requires that the default constructor for base class be defined?

Constructing an object of derived class necessitate to construct an object of its base class (as a derived instance is a base instance + extension).

Thus initializing the derived instance necessitate to initialize the base instance. The question is then when I call a ctor for the derived class, which ctor of the base class is called? As you defined the derived ctor as:

Derived( const Derived & other ){};

the compiler observed that you didn't specified a call to a specific base class ctor, it then generates a call to the ctor with no parameter. But, alas, you deleted it from the base class. It then emits an error.

You may think that calling a copy ctor for the derived class will generates a call to the copy ctor of the base class which wasn't deleted. But, alas, no, the rule is that if you don't specify a specific ctor call for the base class, the ctor with no parameter is called.

What are all the requirements of a QObject derived class?

As Jeremy Friesner suggested, the requirements are not that strict. The situation is more like this:

  • If your class uses signals and/or slots, it must both have the Q_OBJECT macro and be derived from QObject,
  • If it only uses other meta-object functionality, such as Q_PROPERTY declarations, it can use the Q_GADGET macro and need not be derived from QObject,
  • If it doesn't need any of that, but should still be compatible with Qt templates like QVariant, it should be declared with Q_DECLARE_METATYPE. The same applies to enums and Q_ENUM.

A Q_PROPERTY/Q_INVOKABLE interface is only really needed if you need your class to be interoperable with QML code.

As for your other question, yes that is an important difference between QObjects and non-QObjects. The metatype must be copyable, which is why that is required of types you manually declare as metatypes, and also why the system instead uses pointers for QObject types, which are not copyable themselves. A minimal QObject declaration could start like this:

#ifndef CLASSNAME_H
#define CLASSNAME_H

#include <QObject>

// Enums in the global namespace cannot be registered; they must be enclosed
// in a class and registered with Q_ENUM, or in a namespace declared as
// Q_NAMESPACE and registered with Q_ENUM_NS

class ClassName : public QObject
{
Q_OBJECT

public:
// Default constructor, with explicit specifier to prevent accidental
// implicit conversion from a QObject*
explicit ClassName(QObject *parent = nullptr);
};

// ClassName* is automatically declared as a metatype

#endif // CLASSNAME_H

In general I'd recommend the "rule of zero": if possible, do not declare a destructor, nor any copy and move operations, and leave them to the compiler. Of course I also recommend all of the other guidelines there, if you have the time!

Qt: in a sub-class of QThread copy, the constructor is deleted by compiler

In the blog woboq.com/blog/qthread-you-were-not-doing-so-wrong.html, it states that when event loop is needed, we could use worker/controller pattern, otherwise inherit QThread is good.



Related Topics



Leave a reply



Submit