1.前言
在编程中,会用到某些资源,这些资源有的在整个应用程序期间是唯一的;是不能通过拷贝、赋值的方法存在多份的,如STL的std::unique_ptr指针指向的资源。现实中这样的资源有:文件指针、串口句柄等。试想如果存在多个同一个文件的句柄或同一个串口的句柄,就可能在同一时刻对同一文件或串口进行写操作,这样会导致文件内容杂乱、损坏;现实中,当一个串口被一个程序占用时,另外一个程序尝试打开这个串口会报错。为了防止资源通过拷贝、赋值方式存在多份,Qt引入了Q_DISABLE_COPY、Q_DISABLE_MOVE、Q_DISABLE_COPY_MOVE宏。
2.删除函数、拷贝/移动构造函数、拷贝/移动赋值函数
说明:本节都是C++11中的知识,如果读者对本节很熟悉,可以跳过本节的学习。本节只是粗略提到这几个函数的说明,更深入的用法,可以自行学习C++11来进一步深入。
如下代码:
class MyClass
{
private:
MyClass(const MyClass &) = delete; // 拷贝构造函数被删除
MyClass(const MyClass &&) = delete;// 移动构造函数被删除
MyClass &operator=(const MyClass &) = delete; // 拷贝赋值函数被删除
MyClass& operator=(MyClass&&);// 移动赋值函数被删除,注意没有const
};
在上面代码中,声明一个名为 MyClass的类。其中第4、5、6、7行分别为:复制构造函数、移动构造函数、拷贝赋值函数、移动赋值函数。因为这几个函数被标注为delete,所以调用这几个函数会报错。报错类似如下:
尝试引用已删除的函数
C++11正是通过这种方式保证资源无法通过拷贝、赋值方式存在多份。
3.Qt这几个宏的说明
Q_DISABLE_COPY宏等同于2节第4、6行代码功能,即拷贝构造和拷贝赋值函数都被删除。
Q_DISABLE_MOVE宏等同于2节第5、7行代码功能,即移动构造和移动赋值函数都被删除。
Q_DISABLE_COPY_MOVE宏等同于2节第4、5、6、7行代码功能,即拷贝构造、拷贝赋值函数、移动构造、移动赋值函数都被删除。
使用这几个宏的需要注意如下几点:
本类表示的对象不能通过拷贝、赋值的方法存在多份,只能唯一存在。
当使用这几个宏时,将其放到类的private标识区。
这几个宏的参数为类名。
4.附加说明
QObject 中没有提供一个拷贝构造函数和赋值操作符给外界使用,其实拷贝构造和赋值的操作都是已经声明了的,但是它们被使用了Q_DISABLE_COPY() 宏放在了private区域。因此所有继承自QObject的类都使用这个宏声明了他们的拷贝构造函数和赋值操作符为私有。
为什么要这样做?我们都知道Qt对标准C++增加了一些功能:signals, slots, object properties, events, event filters, string translation, timers,object trees, guarded pointers, dynamic cast.
新加入的这些功能就要求我们把每一个QObject的对象看做是唯一(identities)的。唯一的意思就是不能通过拷贝或者赋值操作制作出一个一模一样的复制体。试想如果我们有一个QPushButton对象btnSubmit,如果我们可以复制出一个和btnSubmint完全一样的button对象,那么新的button对象的名字应该是什么?如果也叫btnSubmit,当我们给其中的btnSubmit接收事件或发出信号时,系统如何区分把事件由哪个button对象接收,或者哪个对象发送了信号?
我们知道在各种容器中能以value方式存放的类型,必须有默认的构造函数,拷贝构造函数和赋值操作。由于QObject及所有继承自它的子类都没有提供拷贝构造和赋值操作,当我们使用QList时,编译器就会报错。如果我们要在容器中存储这中类型的对象,我们就要使用它们的指针。如QList<QObject *>。如下代码:
QWidget w1 = QWidget();
根据上述的规则,会报错,报错如下:
转到QWidget类的声明处,可以看到如下:
即QWidget的拷贝构造函数被删除了。