«Попытка ссылки на удаленную функцию» после добавления QMutex в класс - PullRequest
0 голосов
/ 27 июня 2018

Я создаю приложение с помощью Qt5. Моя программа собирается и работает нормально, но есть конфликт между двумя потоками, обращающимися к структуре данных. У меня есть QList объектов CanMessage, и я хочу защитить некоторые данные внутри него, используя QMutex. Однако, как только я добавляю QMutex в определение своего класса, я получаю ошибки:

QList.h: `error: C2280: 
'CanMessage::CanMessage(const CanMessage &)': attempting to reference a deleted function`. 

Вот мой canmessage.h файл:

#ifndef CANMESSAGE_H
#define CANMESSAGE_H

#include <QObject>
#include <QMutex>
#include "cansignal.h"

class CanMessage
{
public:
    CanMessage();
    /* snip - public function prototypes */

private:
    /* snip - private prototypes and data */
    QMutex m_messageMutex;
};

#endif // CANMESSAGE_H

И cansignal.h:

#ifndef CANSIGNAL_H
#define CANSIGNAL_H

#include <QObject>
#include <QDebug>
#include <QByteArray>

class CanSignal
{
public:
    CanSignal();

    CanSignal(QString &signalName, quint8 &signalLength, quint8 &signalStartBit,
                   float &offset, float &factor, bool &isBigEndian, bool &isFloat, bool &isSigned)
    {
        this->m_name = signalName;
        this->m_length = signalLength;
        this->m_startBit = signalStartBit;
        this->m_offset = offset;
        this->m_factor = factor;
        this->m_isBigEndian = isBigEndian;
        this->m_isFloat = isFloat;
        this->m_isSigned = isSigned;
    }

    bool setName(QString &signalName);
    bool setBitLength(quint8 &length);
    bool setStartBit(quint8 &startBit);
    bool setOffset(float &offset);
    bool setFactor(float &factor);
    bool setEndianess(bool &isBigEndian);
    bool setIsFloat(bool &isFloat);
    bool setIsSigned(bool &isSigned);
    void setValid();
    void setInvalid();
    void setEngineeringData(float data);

    QString getName();
    quint8 getBitLength();
    quint8 getStartBit();
    float getOffset();
    float getFactor();
    float getData();
    bool isBigEndian();
    bool isFloat();
    bool isSigned();
    bool getSignalValidity();


private:
    QString m_name;
    quint8 m_length;
    quint8 m_startBit;
    float m_offset;
    float m_factor;
    float m_data_float = 0;
    bool m_isBigEndian;
    bool m_isFloat;
    bool m_isSigned;
    // Set After everything in signal is filled
    bool m_isSignalValid = false;
};

#endif // CANSIGNAL_H

1 Ответ

0 голосов
/ 27 июня 2018
CanMessage::CanMessage(const CanMessage &)

- это конструктор копирования, который, очевидно, используется для помещения элемента в список. Это не сработает, поскольку QMutex не на самом деле копируемый.

Как вы решите это, зависит от многих вещей. Возможно, самым простым способом было бы изменить CanMessage так, чтобы он имел динамический мьютекс (конечно, созданный в конструкторе).

Затем создайте для него конструктор копирования, который сначала блокирует мьютекс объекта source , а затем динамически выделяет новый мьютекс в целевом объекте.

Таким образом, вы можете гарантировать, что старый объект будет "чистым" при копировании (потому что у вас есть его мьютекс), и не будет проблемы "попытки скопировать некопируемый элемент", так как сам мьютекс не скопировано. Подробнее см. Сноску (a).


Следующий код, представляющий собой простой простой фрагмент, показывающий проблему, компилируется нормально, если вы оставите строку QMutex m_mutex; закомментированной:

#include <QList>
#include <QMutex>

#include <iostream>

class Xyzzy {
private:
    int m_xyzzy;
    //QMutex m_mutex;
public:
    Xyzzy() : m_xyzzy(0) {};
    Xyzzy(int val) : m_xyzzy(val) {};
};

int main() {
    QList<Xyzzy> myList;
    Xyzzy plugh;
    myList.push_back(plugh);
    return 0;
}

Как только вы откомментируете эту строку, компилятор справедливо жалуется:

 error: use of deleted function 'Xyzzy::Xyzzy(const Xyzzy&)'

(a) С точки зрения решения проблемы вы можете сделать что-то вроде:

#include <QList>
#include <QMutex>

#include <iostream>

class Xyzzy {
private:
    int m_xyzzy;
    QMutex *m_mutex; // Now a pointer
public:
    Xyzzy() : m_xyzzy(0) {
        m_mutex = new QMutex(); // Need to create in constructor.
        std::cout << "constructor " << m_mutex << '\n';
    };
    ~Xyzzy() {
        std::cout << "destructor " << m_mutex << '\n';
        delete m_mutex; // Need to delete in destructor.
    }
    Xyzzy(const Xyzzy &old) {
        old.m_mutex->lock();
        m_mutex = new QMutex(); // Need to make new one here.
        std::cout << "copy constructor from " << old.m_mutex
                  << " to " << m_mutex << '\n';
        old.m_mutex->unlock();
    }
};

int main() {
    QList<Xyzzy> myList;
    Xyzzy plugh;
    myList.push_back(plugh);
    return 0;
}

Это работает правильно, согласно следующему тесту:

constructor 0x21c9e50
copy constructor from 0x21c9e50 to 0x21c9ec0
destructor 0x21c9e50
destructor 0x21c9ec0

В реальном коде я бы, вероятно, выбрал умные указатели, а не необработанные new/delete вызовы, но это только для иллюстрации концепции. Кроме того, вам нужно будет обработать все другие возможности, которые создают новый объект из существующего, в соответствии с правилом «три / пять / все, что приходит дальше», в настоящее время (из памяти) ограничено элементом назначения копирования Xyzzy &operator=(const Xyzzy &old) .

...