Here we can read that no copy construct and copy assignment operator evaluable. But here we can read that qRegisterMetaType
and Q_DECLARE_METATYPE
have to have public default constructor, public copy constructor and public destructor. The question is: who is telling a lie? Or I did not understand it correctly?
问题:
回答1:
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);
回答2:
Q_DECLARE_METATYPE
macro is used to generate information for custom user types, if you want to use them as SIGNAL/SLOT arguments.
Example:
struct MyInfo
{
QString name;
QDate birthday;
};
Q_DECLARE_METATYPE( MyInfo )
// ... somewhere in cpp:
{
QObject::connect( obj1, SIGNAL( newData( MyInfo ), SLOT( onNewData( MyInfo ) ) );
}
Without Q_DECLARE_METATYPE
macro you could not pass MyInfo
as signal or slot argument.
If you use cross-thread connections (Qt::QueuedConnection
, Qt::BlockingQueuedConnection
, etc) you also need to register your type with qRegisterMetatype<MyInfo>();
call.
But it is ok, to use Q_DECLARE_METATYPE
to register POINTERS to QObjects. For example:
class MyItem
: public QObject
{
Q_OBJECT
//...
};
Q_DECLARE_METATYPE( MyItem * )
// or event better Q_DECLARE_METATYPE( QSharedPointer< MyItem > )
Pointers are POD types, that may be constructed, copied, etc.
回答3:
You can certainly implement a copy constructor and assignment operators in a class that derives from QObject
, but you can't refer to the deleted base class copy constructor and assignment operator. You need to roll your own.
Thus, you need to live with the fact that the copy construction or assignment won't affect the signal/slot connections to either the source or the target object. You must also decide how the parentage is handled when copying - either of the three possible choices are entirely arbitrary - that's why it makes no sense to copy objects in most cases, it's too error prone.
For example, using the copy-and-swap idiom.
class CopyableObject : public QObject
{
Q_OBJECT
public:
friend void swap(CopyableObject & first, CopyableObject & second) {
// d_ptr swap doesn't take care of parentage
QObject * firstParent = first.parent();
QObject * secondParent = second.parent();
first.setParent(0);
second.setParent(0);
first.d_ptr.swap(second.d_ptr);
second.setParent(firstParent);
first.setParent(secondParent);
}
CopyableObject(const CopyableObject & other) : QObject(other.parent()) {
Q_ASSERT(thread() == other.thread());
setObjectName(other.objectName());
blockSignals(other.signalsBlocked());
}
CopyableObject(QObject * parent = 0) : QObject(parent) {}
// C++11 only
#if __cplusplus >= 201103L
CopyableObject(CopyableObject && other) : CopyableObject() {
swap(*this, other);
}
#endif
CopyableObject & operator=(CopyableObject other) {
swap(*this, other);
return *this;
}
};
Note that you need to reimplement the swap
function for derived classes.