Q_DISABLE_COPY、Q_DISABLE_MOVE、Q_DISABLE_COPY_MOVE用法详解及总结

news/2024/11/19 18:19:26/

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正是通过这种方式保证资源无法通过拷贝、赋值方式存在多份。

说明:C++基础不扎实的读者就会发出疑问:既然外层不能调用这些函数,不在cpp文件中实现构造、赋值函数不就行了吗?为何还要费心费力的写那么多标记为delete的函数呢?按照C++的规则:这几个函数如果不手动写,则编译器会为我们默认生成,默认生成的函数按位进行拷贝,反映到拷贝构造函数上,就是浅拷贝,所以不写和手动写但标记为delete是完全不同的。

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.单例模式

对于在整个应用程序中,只能存在一份的对象,在设计中,一般通常采用单例设计模式。如下为Qt

下的单例模式的.h文件

class MyClass:public QObject{private:MyClass(QObject *parent = Q_NULLPTR);~MyClass();MyClass(const MyClass &) = delete; // 拷贝构造函数被删除MyClass(const MyClass &&) = delete;// 移动构造函数被删除MyClass &operator=(const MyClass &) = delete; // 拷贝赋值函数被删除MyClass& operator=(MyClass&&);//  移动赋值函数被删除,注意没有const
private:static MyClass* createAndGetInstance(); // 创建并返回MyClass类对象的唯一实例,也即m_pInstance
private:// 唯一实例static MyClass* m_pInstance;};

上面写了一大堆删除函数,对程序员来说是个负担,有了上面的宏后,则代码可以简写为:

class MyClass:public QObject{private:MyClass(QObject *parent = Q_NULLPTR);~MyClass();
private:Q_DISABLE_COPY_MOVE(MyClass)
private:static MyClass* createAndGetInstance(); // 创建并返回MyClass类对象的唯一实例,也即m_pInstance
private:// 唯一实例static MyClass* m_pInstance;};

关于单例模式的实现,可参考如下链接:

  • 《单例》。

  • 《单例模式(Qt实现)》。

  • 《Q_GLOBAL_STATIC用法及如何保证多线程下的单例模式安全性》。

5.附加说明

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的拷贝赋值函数被删除了。


http://www.ppmy.cn/news/18835.html

相关文章

(考研湖科大教书匠计算机网络)第二章物理层-第一、二节:物理层基本概念和传输媒体

文章目录一&#xff1a;物理层概念二&#xff1a;物理层传输媒体&#xff08;1&#xff09;导引型传输媒体A&#xff1a;同轴电缆B&#xff1a; 双绞线C&#xff1a;光纤①&#xff1a;光纤通信②&#xff1a;光纤D&#xff1a;电力线&#xff08;2&#xff09;非导引型传输媒体…

【实操案例八】元组、集合操作 实例代码及运行效果图!

任务一&#xff1a;我的咖啡馆你做主 方法一&#xff1a;使用列表 # 任务一&#xff1a;我的咖啡馆你做主 # 方法一&#xff1a;使用列表lst[蓝山,卡布奇诺,拿铁,皇家咖啡,女王咖啡,美丽与哀愁]for i in lst:print(lst.index(i)1,.,i,end\t) print()while True:chice int(in…

算法练习笔记——栈的常用方法以及算法练习

栈学习常用方法介绍力扣练习力扣 20. 有效的括号力扣 32. 最长有效括号常用方法介绍 Stack<Character> characters new Stack<>();//判断栈是否为空boolean empty characters.empty();//将a压入栈底&#xff0c;同时也返回aCharacter push characters.push(a);/…

02 |「数据结构、逻辑结构、物理结构」基本概念简析

前言 前言&#xff1a;简析数据结构、逻辑结构、物理结构。 文章目录前言一、数据结构1. 简介2. 数据3. 结构4. 分析5. 分类1&#xff09;线性结构&#xff08;线性表&#xff09;2&#xff09;树结构3&#xff09;图结构二、逻辑结构与物理结构1. 为什么要有逻辑结构和物理结构…

十六进制转八进制+超大数据处理(蓝桥杯基础练习C/C++)

目录 1 题目要求 2 代码 2.1 以十进制为媒介&#xff08;不可行&#xff09; 2.2 以二进制为媒介 3 可能会遇到的难点 4 结论 5 延伸 5.1 超大数据阶乘 5.2 超大数据加法 5.3 以十进制为媒介的进制转换 1 题目要求 2 代码 2.1 以十进制为媒介&#xf…

深信服行为感知命令执行漏洞

深信服行为感知命令执行漏洞1.深信服行为感知漏洞1.1.漏洞描述1.2.漏洞影响1.3.漏洞复现1.3.1.登录页面1.3.2.构建漏洞URL1.3.2.1.查询IP地址1.3.2.2.查询当前目录下文件1.深信服行为感知漏洞 1.1.漏洞描述 深信服 行为感知系统c.php远程命令执行漏洞&#xff0c;使用与EDR相同…

创建者模式-原型模式

1.概述 用一个已经创建的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象相同的新对象 2.结构 原型模式包含如下角色&#xff1a; 抽象原型类&#xff1a;规定了具体原型对象必须实现的的 clone() 方法。具体原型类&#xff1a;实现抽象原型类的 clone() …

liunx centos9安装nodejs并搭建vue 图文详解手把手教程

首先nodjs的官网找到liunx的安装包 https://nodejs.org/en/download/ 这里不推荐用源码安装&#xff0c;因为实在太慢&#xff0c;我安装时一下在不停安装连续15分钟都还在跑就是不知道什么原因 解压包 tar -xvf /root/node-v18.13.0-linux-x64.tar.xz设置全局 -s后面地址就是…