Я строю приложение с 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
CanMessage::CanMessage(const CanMessage &)
это конструктор копирования, который, очевидно, используется для помещения элемента в список. Это не будет работать, так как QMutex
не является на самом деле копируемый.
Как вы решите это, зависит от ряда вещей. Возможно, самый простой способ будет изменить CanMessage
так что он имеет динамический мьютекс (конечно, созданный в конструкторе).
Затем создайте для него конструктор копирования, который сначала блокирует источник объект мьютекс затем динамически распределяет новый мьютекс в целевом объекте.
Таким образом, вы можете гарантировать, что старый объект будет «чистым» при копировании (потому что у вас есть его мьютекс), и не будет проблемы «попытки скопировать некопируемый элемент», так как сам мьютекс не скопировано. Смотрите сноску (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&)'
(А) С точки зрения решения проблемы вы можете сделать что-то вроде:
#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)
,
Других решений пока нет …