1

Qt5 でアプリケーションを作成しています。プログラムは正常にビルドおよび実行されますが、データ構造にアクセスする 2 つのスレッド間で競合が発生します。CanMessage オブジェクトの QList があり、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
4

1 に答える 1

2
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&)'

(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 0x2fff2f0
destructor 0x21c9e50
destructor 0x2fff2f0

実際のコードでは、生の呼び出しではなくスマート ポインターを選択する可能性new/deleteがありますが、これは概念を説明するためだけのものです。さらに、現在 (メモリから) コピー割り当て member に制限されている、3/5/次のルールに従って、既存のオブジェクトから新しいオブジェクトを作成する他のすべての可能性を処理する必要がありますXyzzy &operator=(const Xyzzy &old)

于 2018-06-27T01:10:53.377 に答える