Qt解决error: use of deleted function和is private within this context

文件

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);

}

编译报错

略\QtCore\qlist.h:454: error: 'QLCDNumber::QLCDNumber(const QLCDNumber&)' is private within this context
     if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) n->v = new T(t);
                                                                 ^~~~~~~~
略\QtCore\qlist.h:454: error: use of deleted function 'QLCDNumber::QLCDNumber(const QLCDNumber&)'
     if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) n->v = new T(t);
                                                                 ^~~~~~~~

解决过程

 通过报错,我们可知两个error的字面意思分别是构造函数是私有的使用了析构函数。第一句好理解,就是在不能使用私有函数的地方使用了私有函数得到的报错,第二句没看懂意思,于是进一步往下查询,报错的文件qlist.h中相关代码,/*number- -*/表示编译错误的行数,

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
}

 通过上述文件错误可知,

if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) n->v = new T(t);

 这句话触发了is private within this context,于是进一步查询QLCDNumber::QLCDNumber(const QLCDNumber&)拷贝构造函数本身,发现QLCDNumber包括QLCDNumber的父类(QFrame/QWidget/QObject)中都没有公有(public)的拷贝构造函数,但是发现QLCDNumber类内部有一个私有的宏,

private:
    Q_DISABLE_COPY(QLCDNumber)

 继续追溯该宏,

/*
   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;

 查到这里,根据上述注释便可知道出现一个error的原因了。调用QList的append的时候,想要触发禁止的拷贝构造函数而引发了报错。
 解决方法也很简单,就是在QLCDNumber外再包一层,规避拷贝构造函数这一环。比较简单地,可以将QList < QLCDNumber >改为QList < QLCDNumber* >。

查询根本原因

 虽然知道了为什么报错,但是不知道一个正常的类为何没有拷贝构造函数,于是继续查文档。在QObject的contents中看到了下面一段话,

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. 

 上段内容说了Qt对象模型的介绍中可以找到原因,并且建议我们使用指向QObject(以及QObject子类)的指针,否则可能会试图将QObject相关对象用作值。例如,没有拷贝构造函数,就不能在容器中对QObject相关对象作值存储,必须存储指针。 就是这种情况,引出了本文讨论的问题。
 紧接着,我翻到了Qt对象模型(Object Model )的介绍,找到了下面一段文,这是关于对QObject应该使用标识(identity,根据我的理解,标识就是指向对象的指针)还是值(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. 

 这段话说明了由于Qt对象模型的特性,假如使用value,拷贝(copy)或者赋值(=)的时候会带来许多困扰,因此,QObject和QObject的所有子类都禁用了拷贝构造函数和赋值操作符。

结论

 QObject相关对象使用指针而非值。

发布了60 篇原创文章 · 获赞 18 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/BadAyase/article/details/103407528