Qt use of deleted function ошибка

Для объектов унаследованных от 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 connected signals and/or slots should be transferred over to the copy. If they would be transferred over, it would imply that other QObjects 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 a QObject 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 the QObject::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();
}

62684c9d69a03452049199.jpeg

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

Метки нет (Все метки)


Студворк — интернет-сервис помощи студентам

Всех приветствую !
Есть такой код с пояснениями:

C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
////////////////
// Wrap.h
////////////////
#include "deck.h"
 
class wrap : public QMainWindow
{
....
private:
   QHash <QByteArray, Deck> DIC
   QByteArray HASH;
}
 
////////////////////
// Wrap.cpp
////////////////////
wrap::wrap(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::wrap)
{ 
   ...
   connect(&DIC[HASH], SIGNAL(progress(ushort)), &progressBarF, SLOT(setProgress(ushort))); // Вызывает ошибки : use of deleted function 'Deck::Deck(const Deck&)' и 'QObject::QObject(const QObject&)' is private в файлах qHash.h и qObject.h !!!!!
 
   // Это было добавлено в код просто для теста
   Deck d;
   DIC[HASH] = d; // вызывает ошибку : use of deleted function 'Deck& Deck::operator=(const Deck&)'
   ...
}
 
////////////////
deck.h
////////////////
 
class Deck : public QObject
{
   ....
}

Итак, конструкция «QHash <QByteArray, Deck> DIC», где Deck — объект — приводит к ошибке use of deleted function ‘Deck:: Deck(const Deck&)’. Я конечно понял что тут каким-то образом косвенно удаляется конструктор (и объект ?), но почему, и как это исправить теперь ? Причем если использовать стандартный объект, например QString — никаких проблем не возникает QHash <QByteArray, QString> DIC — компилируется нормально.

Возможно, Я сам где-то затупил в очевидном месте и не вижу этого. В любом случае, сейчас буду сам разбираться, но если кто-то поможет — буду признателен.

 Комментарий модератора 
Для выделения с++ кода Qt используйте тег CPPQT.
Правила именования тем и оформления сообщений в разделе Qt



0



184 / 176 / 57

Регистрация: 25.09.2014

Сообщений: 828

08.02.2017, 16:34

2

Класс Deck полностью?
Макрос Q_OBJECT? Конструктор копирования?



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() ?

Весь класс довольно большой, приведу значимую часть на данный момент:

C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifndef DECK_H
#define DECK_H
 
#include <QObject>
#include <QtWidgets>
 
struct tic {
    QString translate;
 
    uint
        tmr;
};
 
class Deck : public QObject
{
    Q_OBJECT
 
public:
    QHash <QString, tic> DIC;
 
    Deck();
    Deck(const Deck &d);
    ~Deck();
 
    QList <QString> Order(PList p);
 
    tic& operator [](QString i) {return DIC[i];}
};
 
 
Deck::Deck() {
   // в оригинале тоже пусто
}
 
Deck::Deck(const Deck &d) {
   // пока ничего не копируется
}
 
Deck::~Deck() {
   // в оригинале тоже пусто
}
 
#endif



0



1443 / 1326 / 131

Регистрация: 20.03.2009

Сообщений: 4,689

Записей в блоге: 11

08.02.2017, 23:35

4

Цитата
Сообщение от Inversus
Посмотреть сообщение

Или в QObject конструктор копирования «запрещен» ?

Именно так.



1



Горбаг

184 / 176 / 57

Регистрация: 25.09.2014

Сообщений: 828

09.02.2017, 09:50

5

Читайте внимательно документацию к классу QObject.

C++ (Qt)
1
use of deleted function 'Deck& Deck::operator=(const Deck&)'

И оператор= не забудьте вписать для своего класса.
Да и в документации сказано еще вот что:

C++ (Qt)
1
 The key type of a QHash must provide operator==() and a global hash function called qHash() (see qHash).



1



yarko

66 / 66 / 18

Регистрация: 31.03.2015

Сообщений: 253

09.02.2017, 16:32

6

Не проще ли использовать только указатель на свой клас, вместо пихать в контейнер весь клас?

C++ (Qt)
1
QHash <QByteArray, Deck*> DIC;



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

Цитата
Сообщение от Inversus
Посмотреть сообщение

пока не надо

Дело ваше. Но чтобы не гадать потом, откуда вдруг взялись неожиданные ошибки, лучше сразу все сделать по документации.



0



  • Qt platform plugin windows ошибка как исправить windows 10
  • Quik ошибка срок лицензии истек
  • Qualcomm atheros ar3011 bluetooth r adapter код ошибки 43
  • Qt platform plugin windows ошибка как исправить python
  • Quake champions код ошибки 104