将 QMutex 添加到类后“尝试引用已删除的函数”

Posted

技术标签:

【中文标题】将 QMutex 添加到类后“尝试引用已删除的函数”【英文标题】:"Attempting to reference a deleted function" after adding QMutex to class 【发布时间】:2018-06-27 00:55:00 【问题描述】:

我正在使用 Qt5 构建一个应用程序。我的程序构建并运行良好,但访问数据结构的两个线程之间存在冲突。我有一个 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

【问题讨论】:

你能给我们cansignal.h文件吗? CanMessage 是如何使用的? QMutex 不可复制,因此 CanMessage 也不可复制。例如,如果在需要可复制元素的容器中使用 CanMessage,这将不起作用。您的错误消息指的是已删除的复制构造函数,这是有道理的。 是的,请参阅上面的编辑 @RemyLebeau 我有一个他们的 QList。有没有办法让它发挥作用? 手动为CanMessage 定义一个复制构造函数,它不会尝试将QMutex 对象从一个CanMessage 复制到另一个。您可能还需要在运行时动态分配m_messageMutex,这样它的构造就不会影响CanMessage 的构造能力。 【参考方案1】:
CanMessage::CanMessage(const CanMessage &)

是复制构造函数,显然用于将项目放入列表中。这是行不通的,因为QMutex 实际上不可复制。

如何解决它取决于很多事情。也许最简单的方法是修改CanMessage,使其具有动态互斥锁(当然是在构造函数中创建的)。

然后为其创建一个复制构造函数,它首先锁定对象互斥体,然后在目标对象中动态分配一个互斥体。

这样,您可以保证旧对象在复制时是“干净的”(因为您有它的互斥体)并且不会出现“试图复制不可复制的成员”的问题,因为互斥体本身是 not 复制。详情见脚注(a)


如果您将QMutex m_mutex; 行注释掉,以下代码是一个显示问题的完整简单的sn-p,可以正常编译:

#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 调用,但这只是为了说明这个概念。此外,您需要处理所有其他可能性,根据三/五/whatever-comes-next 的规则从现有对象创建新对象,目前(从内存中)仅限于复制分配成员 Xyzzy &amp;operator=(const Xyzzy &amp;old) .

【讨论】:

我明白这一点,但我想知道是否有办法让这个工作,所以我可以有这些对象的 QList void CanBus::addCanMessage(CanMessage &msg) m_messageList.append(msg); m_messageCount++; 谢谢!我会试试这个。 :)

以上是关于将 QMutex 添加到类后“尝试引用已删除的函数”的主要内容,如果未能解决你的问题,请参考以下文章

将来尝试引用已删除的函数

尝试引用已删除的函数时出错

VS2019 C++ 错误 C2280 试图引用已删除的函数 inl

如何修复“在 Qt 中将两个定时器变为一个函数,使用 qmutex 将 qeventloop 进行睡眠”

将ehcache添加到DAO类后Junit测试失败,无法在测试类中实例化DAO

将脚手架项目添加到类库