Для объектов унаследованных от QObject
не генерируется конструктор копирования и оператор копирующего присваивания, потому что они отключены в QObject
при помощи макроса Q_DISABLE_COPY
Например:
class A : public QObject{
};
void foo(){
A a1;
A a2 = a1; //Ошибка
A a3;
a3 = a1; //Ошибка
}
Есть два пути решения проблемы. Первый заключается в том, чтобы написать самому недостающие методы:
class A : public QObject{
public:
A(const A &other):
QObject(other.parent())
{}
A& operator=(const A &other){
return *this;
}
};
void foo(){
A a1;
A a2 = a1; //Работает
A a3;
a3 = a1; //Работает
}
Правда в этом случае копия объекта не будет иметь всех сигнально-слотовых соединений
Второй предполагает избегать копирования и использовать указатели
class A : public QObject{
};
void foo(QObject *parent){
A *a1 = new A(parent);
A *a2 = a1; //Работает
A *a3 = new A(parent);
a3 = a1; //Работает
}
UPD: Для вашего класса получится что-то такое:
class SetMail : public QObject{
Q_OBJECT
public:
QString sender;
QString provider;
QString theme;
QString file_letter;
QString file_attach;
int subscribeId;
explicit SetMail(QObject *parent = 0):
QObject(parent)
{}
SetMail(const SetMail &other):
QObject(other.parent()),
sender(other.sender),
provider(other.provider),
theme(other.theme),
file_letter(other.file_letter),
file_attach(other.file_attach),
subscribeId(other.subscribeId)
{}
SetMail& operator=(const SetMail &other){
sender = other.sender;
provider = other.provider;
theme = other.theme;
file_letter = other.file_letter;
file_attach = other.file_attach;
subscribeId = other.subscribeId;
}
//...
};
If you try to copy a class that derives from a QObject
it will result in a compiler error, e.g.
class MyClass : public QObject {
Q_OBJECT
} my_class;
auto my_class_copy = my_class;
with Qt5 and using C++11 (supporting =delete
):
error: use of deleted function ‘MyClass::MyClass(const MyClass&)’
or with earlier versions:
error: ‘QObject::QObject(const QObject&)’ is private within this context`
This behaviour is by design. But why is the copy constructor (as well as the assignment operator) deleted? What if you still want to copy it? If it’s not copyable is it then movable? The following post will examine these questions as well as explore whether it’s a good practice to repeat the deletion in the custom subclass. Let’s dive in!
There are several reasons why a QObject
can’t be copied. The two biggest reasons are:
QObjects
usually communicate with each other using the signals and slots mechanism. It’s unclear whether the connectedsignals
and/orslots
should be transferred over to the copy. If they would be transferred over, it would imply that otherQObjects
would automatically subscribe to the copy. This would most likely lead to confusion and unwanted side-effects for the developers.QObjects
are organised in object trees. Usually one instance of aQObject
has one parent and several children. Where should the copy be organised in this hierarchy? Should the children (and grandchildren…) also be copied?
Other reasons, but perhaps less critical, are:
- A
QObject
can be considered unique by giving it a name which could be used as a reference key, i.e. by setting theQObject::objectName()
. If the name is set, it’s unclear which name should be given to the copy. QObjects
can be extended with new properties during runtime. Should these new properties also be inherited by the copy?
In general, QObjects
are referred to by their pointer address by other objects. For example, this is the case in the aforementioned signals and slots mechanism
. Because of this, QObjects
can’t be moved; connections between them would then be lost. In the source code of QObject
, we can see that the are no move constructor or move assignment operator declared. However, since the copy constructor is deleted, the move constructor won’t be implicitly generated and an compiler error will be reported if a developer attempts to move a QObject
.
So you can’t copy and you can’t move a QObject
, but what if you desire to copy the underlying data (or properties)? Qt’s documentation distinguish between two object types in the Qt Object Model: value and identity objects. Value objects, such as QSize
, QColor
and QString
are objects that can be copied and assigned. In contrast, the identity objects can’t be copied but can be cloned. As you might have guessed, an example of an identity object is the QOBject
or any class that derives from it. The meaning of cloning can be read from the official documentation:
Cloning means to create a new identity, not an exact copy of the old one. For example, twins have different identities. They may look identical, but they have different names, different locations, and may have completely different social networks.
My understanding of cloning is that you could expose a clone()
-function in a subclass which creates a new identity but not a real copy, i.e:
class MyClass : public QObject {
Q_OBJECT
public:
MyClass* clone() {
auto copy = new MyClass;
//copy only data
return copy;
}
private:
//data
};
...
auto my_class = new MyClass;
auto my_class_clone = my_class->clone();
Although this is possible to do, I wouldn’t recommend it. It could lead to unwanted side-effects as Qt developers will most likely have assumptions about QObjects
. If you have the need of creating a clone, I would suggest to have a look at your overall design and architecture instead. Perhaps the data could be decoupled or factored out?
Repeating Q_DISABLE_COPY(Class)
in the subclass
On stackoverflow it has been suggested to always redeclare the macro Q_DISABLE_COPY(Class)
in your own class, i.e.:
class MyClass : public QObject {
Q_OBJECT
Q_DISABLE_COPY(MyClass) // See macro below
public:
QObject() {}
};
#define Q_DISABLE_COPY(Class)
Class(const Class &);
Class &operator=(const Class &);
The main reason, as mentioned in the stackoverflow post, is to improve the error message. Without the macro, the following error message is reported using Qt4:
error: ‘QObject::QObject(const QObject&)’ is private within this context`
With the macro, it’s reporting:
error: ‘MyClass::MyClass(const MyClass&)’ is private within this context`
The last error message is far more easier to understand for someone who’s new to Qt.
However from Qt5, the macro was changed and declared as:
#ifdef Q_COMPILER_DELETE_MEMBERS
# define Q_DECL_EQ_DELETE = delete
#else
# define Q_DECL_EQ_DELETE
#endif
#define Q_DISABLE_COPY(Class)
Class(const Class &) Q_DECL_EQ_DELETE;
Class &operator=(const Class &) Q_DECL_EQ_DELETE;
Without adding the macro in the subclass, the following error message is displayed:
error: use of deleted function ‘MyClass::MyClass(const MyClass&)’
The copy constructor and assignment operator have now been declared with =delete
instead of just being private, resulting in a preferred error message.
Even though the error message has improved, I still believe it’s valuable to redeclare the macro in the derived class, as it documents the behaviour of the class. Someone who’s new to Qt can quickly understand the intended usage: the object shouldn’t (and can’t) be copied!
Since Qt 5.13 there is a macro that disables both copying and moving the object. The macro makes the wanted behaviour even more explicit and therefore preferred to use. See Q_DISABLE_COPY_MOVE
Пишу клиент-серверное приложение, на этапе написания сервера возникли проблемы с функциями. Не понимаю из за чего может быть проблема
Код программы:
server.h:
#ifndef SERVER_H
#define SERVER_H
#include <QTcpServer>
#include <QTcpSocket>
#include <QVector>
#include <QByteArray>
class Server : public QTcpServer {
Q_OBJECT
public:
Server();
QTcpSocket *socket = nullptr;
private:
QVector<QTcpSocket> sockets;
QByteArray data = nullptr;
void sendToClient(QString text);
public slots:
void incomingConnection(qintptr socketDescriptor);
void slotReadyRead();
};
#endif // SERVER_H
server.cpp:
#include "server.h"
#include <QDataStream>
Server::Server() {
if(this->listen(QHostAddress::Any, 2323)) {
qDebug() << "Server started";
} else {
qDebug() << "Server starting error";
}
}
void Server::incomingConnection(qintptr handle) {
socket = new QTcpSocket;
socket->setSocketDescriptor(handle);
connect(socket, &QTcpSocket::readyRead, this, &Server::slotReadyRead);
connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);
sockets.push_back(*socket);
qDebug() << "Client connected";
}
void Server::slotReadyRead() {
socket = (QTcpSocket*) sender();
QDataStream in_socket(socket);
in_socket.setVersion(QDataStream::Qt_5_12);
if(in_socket.status() == QDataStream::Ok) {
qDebug() << "Reading...";
QString text = nullptr;
in_socket >> text;
qDebug() << text;
} else {
qDebug() << "Datastream error";
}
}
void Server::sendToClient(QString text) {
data.clear();
QDataStream out_socket(&data, QIODevice::WriteOnly);
out_socket.setVersion(QDataStream::Qt_5_12);
out_socket << text;
socket->write(data);
}
main.cpp:
#include <QCoreApplication>
#include "server.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Server server;
return a.exec();
}
file
mainwindow.h
#include <QMainWindow>
#include <QLCDNumber>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_add_clicked();
void on_pushButton_remove_clicked();
private:
Ui::MainWindow *ui;
QList <QLCDNumber> *m_pList;
};
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_pList = new QList <QLCDNumber>;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_add_clicked()
{
QLCDNumber *w = new QLCDNumber();
m_pList->append(*w);
ui->gridLayout->addWidget(w);
}
A compiler error
LQtCoreqlist.h:454: error: 'QLCDNumber::QLCDNumber(const QLCDNumber&)' is private within this context
if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) n->v = new T(t);
^~~~~~~~
LQtCoreqlist.h:454: error: use of deleted function 'QLCDNumber::QLCDNumber(const QLCDNumber&)'
if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) n->v = new T(t);
^~~~~~~~
To solve the process
By reporting an error, we know that the literal meaning of both errors is that the constructor is private and that the destructor is used. In qlist.h, if you use a private function, /*number- */ indicates the number of lines of compilation errors.
template <typename T>
Q_INLINE_TEMPLATE void QList<T>::node_construct(Node *n, const T &t)
{
/*454--*/ if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) n->v = new T(t);
/*455--*/ else if (QTypeInfo<T>::isComplex) new (n) T(t);
#if (defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__IBMCPP__)) && !defined(__OPTIMIZE__)
// This violates pointer aliasing rules, but it is known to be safe (and silent)
// in unoptimized GCC builds (-fno-strict-aliasing). The other compilers which
// set the same define are assumed to be safe.
/*460--*/ else *reinterpret_cast<T*>(n) = t;
#else
// This is always safe, but penaltizes unoptimized builds a lot.
else ::memcpy(n, static_cast<const void *>(&t), sizeof(T));
#endif
}
According to the error in the above document,
if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) n->v = new T(t);
This sentence triggers is private within this context, so further query QLCDNumber::QLCDNumber(const QLCDNumber&) Copy the constructor itself, found that there is no public copy constructor in QLCDNumber including QLCDNumber’s parent class (QFrame/QWidget/QObject), but found that there is a private macro inside QLCDNumber class,
private:
Q_DISABLE_COPY(QLCDNumber)
Continue tracing the macro,
/*
Some classes do not permit copies to be made of an object. These
classes contains a private copy constructor and assignment
operator to disable copying (the compiler gives an error message).
*/
#define Q_DISABLE_COPY(Class)
Class(const Class &) Q_DECL_EQ_DELETE;
Class &operator=(const Class &) Q_DECL_EQ_DELETE;
At this point, you can see the reason for an error based on the above comment. When calling the append of QList, an attempt to trigger the forbidden copy constructor resulted in an error. The
solution is also simple: wrap a layer over the QLCDNumber to avoid the copy constructor loop. Quite simply, you can put QList < QLCDNumber > Instead of QList & lt; QLCDNumber* > .
Query root cause
You know why the error is being reported, but you don’t know why a normal class doesn’t have a copy constructor, so you go ahead and look at the documentation. In contents on QObject I see the following passage,
No Copy Constructor or Assignment Operator
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.
The main consequence is that you should use pointers to QObject (or to your QObject subclass) where you might otherwise be tempted to use your QObject subclass as a value. For example, without a copy constructor, you can't use a subclass of QObject as the value to be stored in one of the container classes. You must store pointers.
The previous section said that the Qt object model can be found in the introduction, and suggested that we use Pointers to QObject (and QObject subclasses), otherwise we might try to use QObject-related objects as values. For example, without a copy constructor, a qObject-related object cannot be stored as a value in the container; Pointers must be stored. It is this situation that leads to the question discussed in this article.
then, I turned to the introduction of Qt Object Model and found the following paragraph. This is a discussion about whether QObject should use identity (according to my understanding, an identity is a pointer to an Object) or value.
Qt Objects: Identity vs Value
Some of the added features listed above for the Qt Object Model, require that we think of Qt Objects as identities, not values. Values are copied or assigned; identities are cloned. Cloning means to create a new identity, not an exact copy of the old one. For example, twins have different identities. They may look identical, but they have different names, different locations, and may have completely different social networks.
Then cloning an identity is a more complex operation than copying or assigning a value. We can see what this means in the Qt Object Model.
A Qt Object...
might have a unique QObject::objectName(). If we copy a Qt Object, what name should we give the copy?
has a location in an object hierarchy. If we copy a Qt Object, where should the copy be located?
can be connected to other Qt Objects to emit signals to them or to receive signals emitted by them. If we copy a Qt Object, how should we transfer these connections to the copy?
can have new properties added to it at runtime that are not declared in the C++ class. If we copy a Qt Object, should the copy include the properties that were added to the original?
For these reasons, Qt Objects should be treated as identities, not as values. Identities are cloned, not copied or assigned, and cloning an identity is a more complex operation than copying or assigning a value. Therefore, QObject and all subclasses of QObject (direct or indirect) have their copy constructor and assignment operator disabled.
QObject and all subclasses of QObject disable copy constructors and assignment operators due to the nature of the Qt object model, which can cause a lot of confusion when using value, copying, or assignment (=).
conclusion
Qobject-related objects use Pointers instead of values.
Inversus 0 / 0 / 0 Регистрация: 28.08.2016 Сообщений: 16 |
|||||||||
1 |
|||||||||
08.02.2017, 16:04. Показов 1489. Ответов 7 Метки нет (Все метки)
Всех приветствую !
Итак, конструкция «QHash <QByteArray, Deck> DIC», где Deck — объект — приводит к ошибке use of deleted function ‘Deck:: Deck(const Deck&)’. Я конечно понял что тут каким-то образом косвенно удаляется конструктор (и объект ?), но почему, и как это исправить теперь ? Причем если использовать стандартный объект, например QString — никаких проблем не возникает QHash <QByteArray, QString> DIC — компилируется нормально. Возможно, Я сам где-то затупил в очевидном месте и не вижу этого. В любом случае, сейчас буду сам разбираться, но если кто-то поможет — буду признателен.
0 |
184 / 176 / 57 Регистрация: 25.09.2014 Сообщений: 828 |
|
08.02.2017, 16:34 |
2 |
Класс Deck полностью?
0 |
Inversus 0 / 0 / 0 Регистрация: 28.08.2016 Сообщений: 16 |
||||
08.02.2017, 17:59 [ТС] |
3 |
|||
Q_OBJECT присутствовал, а вот конструктора копирования не было. Действительно, при его добавлении ругань на «connect» прекратилась, но при присваивании «DIC[HASH] = d» та же ошибка. Не совсем понимаю для чего вручную описывать конструктор копирования ? Как Я понимаю C++ автоматом создает его со всеми необходимыми присваиваниями ? Или в QObject конструктор копирования «запрещен» ? Возможно ли наследование от чего-то другого ? И Я так понял придется перегружать QHash::Hash() ? Весь класс довольно большой, приведу значимую часть на данный момент:
0 |
1443 / 1326 / 131 Регистрация: 20.03.2009 Сообщений: 4,689 Записей в блоге: 11 |
|
08.02.2017, 23:35 |
4 |
Или в QObject конструктор копирования «запрещен» ? Именно так.
1 |
Горбаг 184 / 176 / 57 Регистрация: 25.09.2014 Сообщений: 828 |
||||||||
09.02.2017, 09:50 |
5 |
|||||||
Читайте внимательно документацию к классу QObject.
И оператор= не забудьте вписать для своего класса.
1 |
yarko 66 / 66 / 18 Регистрация: 31.03.2015 Сообщений: 253 |
||||
09.02.2017, 16:32 |
6 |
|||
Не проще ли использовать только указатель на свой клас, вместо пихать в контейнер весь клас?
0 |
0 / 0 / 0 Регистрация: 28.08.2016 Сообщений: 16 |
|
09.02.2017, 21:52 [ТС] |
7 |
use of deleted function ‘Deck& Deck::operator=(const Deck&)’ Да, протупил с этим. Оператор = вписал, все ок, спасибо, плюсики поставил. По поводу хеш-функции — пока не надо, ключи нативные.
0 |
184 / 176 / 57 Регистрация: 25.09.2014 Сообщений: 828 |
|
10.02.2017, 09:10 |
8 |
пока не надо Дело ваше. Но чтобы не гадать потом, откуда вдруг взялись неожиданные ошибки, лучше сразу все сделать по документации.
0 |