Qt面试题

1.QT信号槽机制的优缺点

优点:

1.类型安全:需要关联的信号槽的签名必须是等同的,即信号的参数类型和参数个数和接受该信号的槽的参数类型和参数个数相同。(PS:信号函数的参数个数必须大于等于槽函数的参数个数

2.松散耦合:发射信号的对象不需要知道哪个槽接收,也不需要知道是否被接受,只需要适当的时候发送就行了。QT保证适当的槽得到调用,即使关联的对象在运行时被删除,程序也不会崩溃

3.灵活性:一个信号可以关联多个槽,多个信号也可以关联同一个槽

缺点:

1.速度较慢:与回调函数相比,信号和槽机制运行速度比直接调用非虚函数慢10倍左右,原因:

  • 需要定位接收信号的对象
  • 安全的遍历所有槽
  • 多线程的时候,信号需要排队等候

2.信号槽的参数限定很多,例如不能携带模板类参数,不能出现宏定义等。

2.多线程情况下, Qt中的信号槽分别在什么线程中执行, 如何控制?

可以通过connect函数的第五个参数来控制,信号槽执行时所在的线程

1)自动连接(AutoConnection),默认的连接方式,如果信号与槽,也就是发送者与接受者在同一线程,等同于直接连接;如果发送者与接收者处在不同线程,等同于队列连接。

2)直接连接(DirectConnection),当信号发射时,槽函数立即直接调用。无论槽函数所属对象在哪个线程,槽函数总在发送者所在线程执行,即槽函数和信号发送者在同一线程

3)队列连接(QueuedConnection),信号发出后,信号会暂时放在一个消息队列里,需等到接收对象所属线程的事件循环取得控制权才能取得信号,然后执行相关联的槽函数。

4)阻塞队列连接(Qt::BlockingQueuedConnection):槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。而且接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。

5)唯一连接(Qt::UniqueConnection):这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是为了避免重复连接。

 3.描述QT中的文件流(QTextStream)和数据流(QDataStream)的区别

文件流:操作轻量级数据(int,double,QString)写入文本文件中以后以文本的方式呈现。

数据流:通过数据流可以操作各种数据类型,包括对象,存储到文件中数据为二进制。

文件流和数据流都可以操作磁盘文件和内存数据。

4.描述下QT下TCP通信的整个流程

服务器端:

1.创建用于监听的套接字并绑定端口和ip地址

2.给套接字设置监听

3.如果有连接到来,服务器端发出newconnected()信号

4.接收连接,通过nextPendingConnection()函数,返回一个QTcpSocket类型的套接字对象(用于通信)

5.使用用于通信的套接字对象通信

发送数据:write

接收数据:readAll/read

客户端:

1.创建用于通信的套接字

2.连接服务器connectToHost

3.连接成功与服务器通信

发送数据:write

接收数据:readAll/read

5.描述QT下UDP通信的整个流程

1.创建套接字对象

2.如果需要接收数据,必须绑定端口

3.发送数据:writeDatagram

4.接收数据:readDatagram

6.描述QT下多线程的两种使用方法,以及注意事项

第一种方法:

1.创建一个类从QThread类派生

2.在子类线程类中重写run函数,将处理操作写入该函数中

3.在主线程中创建子线程对象,启动子线程,调用start函数

第二种方法:

1.将业务处理抽象成一个业务类,在该类中创建一个业务处理函数

2.在主线程中创建一个QThread类对象

3.在主线程中创建一个业务类对象

4.将业务类对象移动到子线程中

5.在主线程中启动子线程

6.通过信号槽的方式,执行业务类中的业务处理函数

7.QT的优点、缺点

优点:

1.跨平台,几乎支持所有平台

2.接口简单,文档详细

3.开发效率高

缺点:

QT作为一个软件平台,比较庞大,臃肿

8.Qt的核心机制

信号和槽机制,在QT中使用信号和槽机制代替了回调机制。信号会在特定的事件出现时被发出,槽是在响应特定信号时会被调用的方法。

9.信号和槽机制原理

1.QT中的元对象编译器(moc)查找头文件中的信号和槽,标记出信号和槽

2.将信号和槽信息存储到类静态变量staticMetaObject中,并且按声明顺序存放,建立索引

3.当发现有connect连接时,将信号和槽的索引信息放到一个map中,彼此配对

4.当调用emit时,调用信号函数,并且传达发送信号的对象指针,信号索引,元对象指针,参数列表到activate函数

5.通过activate函数找到map中找到所有与信号对应的槽索引

6.根据槽索引找到槽函数,执行槽函数

10.信号和槽的本质是什么

回调函数。

信号:传递值或传递动作变化;槽函数响应信号或者接收值,或根据动作变化来做出相应操作

11.信号和槽与函数指针的比较

1.回调函数使用函数指针来实现,如果多个类都关注一个类的动态变化,这样就会需要写出一个比较长的列表来管理这些类之间的关系,在编码方面不灵活

2.QT使用信号与槽来解决这个连接问题,这种方式比较清晰简单一些,一个类只需要清楚自己有几个槽函数有几个信号,然后将信号与槽进行连接,QT会自己处理函数的调用关系。这样在软件设计角度更加的清晰,灵活,不容易出错。

3.Qt信号与槽机制降低了Qt对象的耦合度。发信号的对象不需要知道有几个槽函数,也不需要关系是否收到信号,或者谁收到了,谁没收到。同样的槽函数也不需要知道谁是信号的发出者。信号只需要在合适的时机发出即可,降低了对象之间的耦合度。

12.QT事件过滤器

1.父窗口类通过重写eventFilter方法来监听子控件的相关事件进行处理。 使用这种方式的好处是不需要通过重写控件的方式获取某些事件,对于安装了事件过滤器的对象,他们所有的事件都会经过这个事件过滤器,所以就可以直接在父窗口中进行监测。比如某个窗口中有个QLabel对象,我们想要监听他的鼠标点击事件,那我们就需要继承QLabel类,然后重写mousePressEvent事件,如果有其他类型的控件也需要获取某个事件,那是不是都需要继续控件并重写某个事件了,所以我们通过事件过滤器就可以很方便获取某个对象的某个事件。

2.专门的事件过滤器类,对特定的对象/特定的事件进行处理。 事件过滤器类只需对当前安装的对象进行处理,无需关心其他操作,且一个事件过滤器类可以被多个对象使用,例如Qt文档中的按键过滤示例,KeyPressEater类中的eventFilter过滤了所有的键盘按下事件,只要安装此事件过滤器的控件,都接收不到键盘按键按下的事件,这种就是对某个通用的事件进行过滤,可以进行多次复用。

3.给QApplication安装事件过滤器,达到全局事件监听的效果。 在notify方法下发事件的时候,QApplication对象可以拿到第一控制权,对某些事件优先进行处理,比如全局的快捷键操作。

(好处:不用针对每个控件都重写某个事件,只需要对需要使用该事件的控件安装事件过滤器就行,还可以一个事件过滤器类可以被多个对象使用)

注意点:

1.一个对象可以安装多个事件过滤器(也就是一个对象的事件可以被多个对象进行监控/处理/过滤), 并且最先安装的事件过滤器是最后被调用的,类似于栈的操作,先进后出;

2.一个事件过滤器可以被多个对象安装,但是如果在事件过滤器(eventFilter方法)中把该对象删除了, 一定要将返回值设为true。否则 Qt会将事件继续分发给这个对象,从而导致程序崩溃。

13.为什么new QWidget不需要delete

Qt提供了一种机制,能够自动、有效的组织和管理继承自QObject的Qt对象,这种机制就是对象树。 Qt库中的很多类都以QObject作为它们的基类。QObject的对象总是以树状结构组织自己。当我们创建一个QObject对象时,可以指定其父对象(也被称为父控件),新创建的对象将被加入到父对象的子对象(也被称为子控件)列表中。当父对象被析构时,这个列表中的所有子对象会被析构。不但如此,当某个QObject对象被析构时,它会将自己从父对象的列表中删除,以避免父对象被析构时,再次析构自己。

14.信号和槽的几种连接方式优缺点

1.使用SIGNAL和SLOT宏(宏函数将信号和槽函数转换为const char *类型的字符串)

connect(ui->pushbutton,SIGNAL(clicked),this,SLOT(onPushButtonClicked()))

2.使用&类名::函数名

connect(ui->pushbutton,&QPushButton::clicked,this,onSetBlockedSignalStatus)

优点:

        1.不需要写参数更简便

        2.不需要槽函数的参数类型与信号对应的参数类型完全一致,只需要进行隐藏式转换

        3.可以在编译时进行检查,比如信号或者槽函数的拼写错误、槽函数参数数量多于信号的参数数量等都能在编译时期发现,而不是运行时。

3.Lambda表达式,关联后直接编写信号发射后要执行的代码,不需要定义槽函数

15.事件与信号的区别

1.使用场合和时机不同 一般情况下,在“使用”窗口部件时,我们经常需要使用信号,并且会遵循信号与槽的机制;而在“实现”窗口部件时,我们就不得不考虑如何处理事件了。举个例子,当使用 QPushButton 时,我们对于它的 clicked()信号往往更为关注,而很少关心促成发射该信 号的底层的鼠标或者键盘事件。但是,如果要实现一个类似于 QPushButton 的类,我们就需要编写一定的处理鼠标和键盘事件的代码,而且在必要的时候,仍然需要发射和接收 clicked()信号。

2.使用的机制和原理不同 事件类似于 Windows 里的消息,它的发出者一般是窗口系统。相对信号和槽机制,事件 比较“底层”,它同时支持异步和同步的通信机制,一个事件产生时将被放到事件队列 里,然后我们就可以继续执行该事件 “后面”的代码。事件的机制是非阻塞的。 信号和槽机制相对而言比较“高层”,它的发出者一般是对象。从本质上看,它类似 于传统的回调机制,是不支持异步调用的

16.信号和槽机制需要注意的问题

信号与槽机制是比较灵活的,但有些局限性我们必须了解,这样在实际的使用过程中才能够做到有的放矢,避免产生一些错误。下面就介绍一下这方面的情况。

1.信号与槽的效率是非常高的,但是同真正的回调函数比较起来,由于增加了灵活 性,因此在速度上还是有所损失,当然这种损失相对来说是比较小的,通过在一台 i586- 133 的机器上测试是 10 微秒(运行 Linux),可见这种机制所提供的简洁性、灵活性还是 值得的。但如果我们要追求高效率的话,比如在实时系统中就要尽可能的少用这种机制。

2.信号与槽机制与普通函数的调用一样,如果使用不当的话,在程序执行时也有可能 产生死循环。因此,在定义槽函数时一定要注意避免间接形成无限循环,即在槽中再次发射 所接收到的同样信号。

3.如果一个信号与多个槽相关联的话,那么,当这个信号被发射时,与之相关的槽被 激活的顺序将是随机的,并且我们不能指定该顺序。

4.宏定义不能用在 signal 和 slot 的参数中。

5.构造函数不能用在 signals 或者 slots 声明区域内。

6.函数指针不能作为信号或槽的参数。

7.信号与槽不能有缺省参数。

8.信号与槽也不能携带模板类参数。

17.信号的注意点

1.所有的信号声明都是公有的,所以Qt规定不能在signals前面加public,private, protected。

2.所有的信号都没有返回值,所以返回值都用void。

3.所有的信号都不需要定义。

4.必须直接或间接继承自QOBject类,并且开头私有声明包含Q_OBJECT。

5.在同一个线程中,当一个信号被emit发出时,会立即执行其槽函数,等槽函数执行完毕后,才会执行emit后面的代码,如果一个信号链接了多个槽,那么会等所有的槽函数执行完毕后才执行后面的代码,槽函数的执行顺序是按照它们链接时的顺序执行的。不同线程中(即跨线程时),槽函数的执行顺序是随机的。

6.在链接信号和槽时,可以设置链接方式为:在发出信号后,不需要等待槽函数执行完,而是直接执行后面的代码,是通过connect的第5个参数。

7.信号与槽机制要求信号和槽的参数一致,所谓一致,是参数类型一致。如果不一致,允许的情况是,信号的参数可以比槽函数的参数多,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少),但是不能说信号根本没有这个数据,你就要在槽函数中使用(就是槽函数的参数比信号的多,这是不允许的)。

18.QT中的多线程

qt中的线程与线程同步-CSDN博客

多线程理解-CSDN博客

QMutex、QMutexLocker、QReadWriteLock、QWaitCondition 

1)为什么需要多线程?

如果一个程序中只有一个主线程,在按照代码的执行顺序去执行代码时,假设程序中有一处需要花费大量时间去处理数据的代码,那么整个进程都要停下来去等待数据处理完成才可以继续执行下去,影响客户体验。如果想要消除等待,就可以开辟一个子线程把数据处理部分丢进去,让子线程来处理数据,主线程继续执行剩余的代码。

2)多线程的优缺点

优点:

a)使用线程可以把占据时间长的程序中的任务放到后台去处理

b)程序的运行效率会提高

c)在一些等待的任务上如文件读取和网络收发数据等,线程就比较有用

d)用户体验更好

缺点:

a)如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换

b)更多的线程需要更多的内存空间

c)线程的中止需要考虑对程序运行的影响

d)数据在多个线程间共享,需要防止线程死锁情况的发生

3)线程池

Qt 多线程基础及线程使用方式-CSDN博客

a)为什么要线程池?

目前的大多数网络服务器,包括Web服务器、Email服务器以及数据库服务器等都具有一个共同点,就是单位时间内必须处理数目巨大的连接请求,但处理时间却相对较短。
传统多线程方案中我们采用的服务器模型则是一旦接受到请求之后,即创建一个新的线程,由该线程执行任务。任务执行完毕后,线程退出,这就是是“即时创建,即时销毁”的策略。尽管与创建进程相比,创建线程的时间已经大大的缩短,但是如果提交给线程的任务是执行时间较短,而且执行次数极其频繁,那么服务器将处于不停的创建线程,销毁线程的状态。

线程池优点:

1.提高性能与资源的利用率

2.解决线程频繁创建与销毁的开销

3.解决线程竞争问题

过多的线程可能导致线程竞争,影响系统性能。线程池通过维护一个可控制的并发数量,有助于减轻线程之间的竞争。

QRunnable

使用线程池需要先创建任务,添加到线程池中的任务 需要QRunnable类型因此需要创建子类继承QRunnable类,然后重写run()方法,在该函数编写在线程池中执行的任务,并将子类对象传递给线程池,这样线程就可以被线程池中的某个工作线程处理掉。

QThreadPool

Qt 中的 QThreadPool 类管理了一组 QThreads, 里边还维护了一个任务队列。QThreadPool 管理和回收各个 QThread 对象,以帮助减少使用线程的程序中的线程创建成本。==每个Qt应用程序都有一个全局 QThreadPool 对象,可以通过调用 globalInstance() 来访问它。==也可以单独创建一个 QThreadPool 对象使用。

4)死锁

什么是线程死锁?形成条件是什么?如何避免?-阿里云开发者社区 (aliyun.com)

多线程死锁的原因以及解决方法-CSDN博客

什么是死锁?

当多个线程访问共享资源时,需要加锁,如果锁使用不当,就会造成死锁这种现象。线程死锁造成的后果:所有的线程都被阻塞,并且线程的阻塞是无法解开的(因为可以解锁的线程也阻塞了)。

造成死锁的场景:

1.加锁之后忘记解锁

2.重复加锁,造成死锁

3.程序中有多个共享资源,因此有很多锁,随意加锁,导致互相被阻塞

形成死锁的四个必要条件是什么:

(1)互斥条件:线程(进程)对于所分配到的资源具有排它性,即一个资源只能被一个线程(进程)占用,直到被该线程(进程)释放
(2)请求与保持条件:一个线程(进程)因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:线程(进程)已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
(4)循环等待条件:当发生死锁时,所等待的线程(进程)必定会形成一个环路(类似于死循环),造成永久阻塞

如何避免死锁:

我们只要破坏产生死锁的四个条件中的其中一个就可以了。
破坏互斥条件
这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。
破坏请求与保持条件
一次性申请所有的资源。
破坏不剥夺条件
占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
破坏循环等待条件
靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。
我们对线程 2 的代码修改成下面这样就不会产生死锁了。

19.详解Qt中的内存管理机制

1.所有继承自QOBJECT类的类,如果在new的时候指定了父亲,那么它的清理时在父亲被delete的时候delete的,所以如果一个程序中,所有的QOBJECT类都指定了父亲,那么他们是会一级级的在最上面的父亲清理时被清理,而不用自己清理;

2.程序通常最上层会有一个根的QOBJECT,就是放在setCentralWidget()中的那个QOBJECT,这个QOBJECT在 new的时候不必指定它的父亲,因为这个语句将设定它的父亲为总的QAPPLICATION,当整个QAPPLICATION没有时它就自动清理,所以也无需清理。9这里QT4和QT3有不同,QT3中用的是setmainwidget函数,但是这个函数不作为里面QOBJECT的父亲,所以QT3中这个顶层的QOBJECT要自行销毁)。

3.这是有人可能会问那如果我自行delete掉这些QT接管负责销毁的指针了会出现什么情况呢,如果时这样的话,正常情况下QT的拥有这个对象的那个父亲会知道这件事情,它会直到它的儿子被你直接DELETE了,这样它会将这个儿子移出它的列表,并且重新构建显示内容,但是直接这样做时有风险的!也就是要说的下一条

4.当一个QOBJECT正在接受事件队列时如果中途被你DELETE掉了,就是出现问题了,所以QT中建议大家不要直接DELETE掉一个 QOBJECT,如果一定要这样做,要使用QOBJECT的deleteLater()函数,它会让所有事件都发送完一切处理好后马上清除这片内存,而且就算调用多次的deletelater也不会有问题。

5.QT不建议在一个QOBJECT 的父亲的范围之外持有对这个QOBJECT的指针,因为如果这样外面的指针很可能不会察觉这个QOBJECT被释放,会出现错误,如果一定要这样,就要记住你在哪这样做了,然后抓住那个被你违规使用的QOBJECT的destroyed()信号,当它没有时赶快置零你的外部指针。当然我认为这样做是及其麻烦也不符合高效率编程规范的,所以如果要这样在外部持有QOBJECT的指针,建议使用引用或者用智能指针,如QT就提供了智能指针针对这些情况,见***一条。

6.QT中的智能指针封装为QPointer类,所有QOBJECT的子类都可以用这个智能指针来包装,很多用法与普通指针一样,可以详见QT assistant 通过调查这个QT的内存管理功能,发现了很多东西,现在觉得虽然这个QT弄的有点小复杂,但是使用起来还是很方便的,***要说的是某些内存泄露的检测工具会认为QT的程序因为这种方式存在内存泄露,发现时大可不必理会。

20.QPixmap、QImage、QPicture、QBitmap的区别

1.QPixmap专门为图像在屏幕上的显示做了优化

2.QBitmap是QPixmap的一个子类,它的色深限定为1,可以使用 QPixmap的isQBitmap()函数来确定这个QPixmap是不是一个QBitmap。

3.QImage专门为图像的像素级访问做了优化。

4.QPicture则可以记录和重现QPainter的各条命令。
图片绘图类QPixmap、QImage、QPicture-CSDN博客

21..qrc,.ui文件格式和用途?QResource资源文件系统的优势

qrc 文件是基于 XML 格式的资源系统配置文件,该文件中指定了各种资源的信息。

ui文件是一个 XML 文件,定义了窗口上的所有组件的属性设置、布局,及其信号与槽函数的关联等

Qt资源系统是一种将图片、数据存储于二进制文件中的一套系统。这些图片、数据会被我们的程序使用,它们称为资源。Qt资源系统中存储了这些资源,使得程序可以很方便地找到并使用它们。

22.show()、exec()的区别

show():显示一个非模式对话框。控制权即刻返回给调用函数。

exec():显示一个模式对话框,并且锁住程序直到用户关闭该对话框为止。函数返回一个DialogCode结果。

23.dleteLater和delete的区别

1.delete:在QT中,delete可以用于释放动态分配的QObject对象。使用delete会立即删除对象,但是如果在对象的生命周期内,仍有其他对象与此对象有关联,那么这些对象可能会访问已经释放的内存吗,导致程序崩溃

2.deleteLater():deleteLater是QObject类中的一个成员函数,用于在一个事件循环中异步删除对象。使用deleteLater()会在对象所属的线程的事件循环中添加一个事件,当事件循环处理完当前事件后,才会执行对象的删除。使用deleteLater()可以避免在对象的生命周期内,其他对象访问已经释放的内存的问题

因此,使用deleteLater()可以更加安全的删除QObject对象,避免程序崩溃的风险。但是,某些情况下,如果需要立即删除对象,使用delete可能更好。

24.QT提供的进程通信方式有哪些

1.信号和槽(Signals and Slots):他通过连接一个对象的信号和另一个对象的槽函数来实现两个对象之间的通信。可以在同一个进程内或不同进程之间发送和接收信号

2.共享内存(Shared Memory):使用QSharedMemory类可以在进程之间共享内存,以便他们可以访问和修改相同的数据

3.TCP/IP套接字(TCP/IP Sockets):QT提供了QTcpSocket和QTcpServer类,可以用于在不同计算机或同一计算机上的不同进程之间建立TCP/IP连接。

25.QT中的智能指针

Qt 的智能指针包括:(看c++11中的智能指针更好理解)

c++面试题-CSDN博客

  • QSharedPointer
  • QScopedPointer
  • QScopedArrayPointer
  • QWeakPointer
  • QPointer
  • QSharedDataPointer

QSharedPointer:

QSharedPointer内部维持着对拥有的内存资源的引用计数,当引用计数下降到0时,这个内存资源就被释放了。

QSharedPointer 的构造函数有如下这几种。

QSharedPointer();
QSharedPointer(X *ptr);
QSharedPointer(X *ptr, Deleter deleter);
QSharedPointer(std::nullptr_t);
QSharedPointer(std::nullptr_t, Deleter d);
QSharedPointer(const QSharedPointer<T> &other);
QSharedPointer(const QWeakPointer<T> &other);

QSharedPointer 是线程安全的,因此即使有多个线程同时修改 QSharedPointer 对象也不需要加锁。这里要特别说明一下,虽然 QSharedPointer 是线程安全的,但是 QSharedPointer 指向的内存区域可不一定是线程安全的。所以多个线程同时修改 QSharedPointer 指向的数据时还要应该考虑加锁的。

下面是个 QSharedPointer 的例子,演示了如何自定义 Deleter。

  static void doDeleteLater(MyObject *obj)
  {
      obj->deleteLater();
  }

  void otherFunction()
  {
      QSharedPointer<MyObject> obj = QSharedPointer<MyObject>(new MyObject, doDeleteLater);

      // continue using obj
      obj.clear();    // calls obj->deleteLater();
  }

QScopedPointer:

QScopedPointer 类似于 C++ 11 中的 unique_ptr。当我们的内存数据只在一处被使用,用完就可以安全的释放时就可以使用 QScopedPointer。只要出了作用域,指针就会被自动删除。

void myFunction(bool useSubClass)
  {
      MyClass *p = useSubClass ? new MyClass() : new MySubClass;
      QIODevice *device = handsOverOwnership();

      if (m_value > 3) {
          delete p;
          delete device;
          return;
      }

      try {
          process(device);
      }
      catch (...) {
          delete p;
          delete device;
          throw;
      }

      delete p;
      delete device;
  }

如果用 QScopedPointer,就可以简化为:

void myFunction(bool useSubClass)
  {
      // assuming that MyClass has a virtual destructor
      QScopedPointer<MyClass> p(useSubClass ? new MyClass() : new MySubClass);
      QScopedPointer<QIODevice> device(handsOverOwnership());

      if (m_value > 3)
          return;

      process(device);
  }

QScopedArrayPointer:

如果我们指向的内存数据是一个数组,这时可以用 QScopedArrayPointer。QScopedArrayPointer 与 QScopedPointer 类似,用于简单的场景。只要出了作用域,指针就会被自动删除。

void foo()
  {
      QScopedArrayPointer<int> i(new int[10]);
      i[2] = 42;
      ...
      return; // our integer array is now deleted using delete[]
  }

QPointer:

QPointer 与其他的智能指针有很大的不同。其他的智能指针都是为了自动释放内存资源而设计的。 QPointer 智能用于指向 QObject 及派生类的对象。当一个 QObject 或派生类对象被删除后,QPointer 能自动把其内部的指针设为 0。这样我们在使用这个 QPointer 之前就可以判断一下是否有效了。

为什么非要是 QObject 或派生类呢,因为 QObject 可以构成一个对象树,当这颗对象树的顶层对象被删除时,它的子对象自动的被删除。所以一个 QObject 对象是否还存在,有时并不是那么的明显。有了 QPointer 我们在使用一个对象之前,至少可以判断一下。

要特别注意的是,当一个 QPointer 对象超出作用域时,并不会删除它指向的内存对象。这和其他的智能指针是不同的。我们在手动delete一个指针的时候,需要再将其置空,要不然会变成一个悬挂的野指针,那么QPointer就是帮忙干这事的,会在对象被销毁时,自动设置为NULL。

下面给一个简单的例子:
 

    QPointer<QLabel> label = new QLabel;
      label->setText("&Status:");
      ...
      if (label)
          label->show();

QSharedDataPointer

隐式数据共享

共享类由指向共享数据块的指针组成,该指针包含引用计数和数据。 

在处理共享对象时,有两种复制对象的方法:深拷贝和浅拷贝。

深拷贝意味着复制一个对象。
浅拷贝是引用拷贝,即只是指向共享数据块的指针。
就内存和 CPU 而言,进行深度复制可能会很昂贵。制作浅拷贝非常快,因为它只涉及设置指针和增加引用计数。

隐式共享对象的对象分配(使用 operator=())是使用浅拷贝实现的。

共享的好处是程序不需要不必要地复制数据,从而减少内存使用和数据复制。对象可以很容易地被赋值,作为函数参数发送,并从函数中返回。

在实现自定义的隐式共享类时,请使用 QSharedData 和 QSharedDataPointer 类。

QSharedDataPointer 这个类是帮我们实现数据的隐式共享的。我们知道 Qt 中大量的采用了隐式共享和写时拷贝技术。比如下面这个例子:

QString str1 = "abcdefg";
QString str2 = str1;
QString str2[2] = 'X';

第二行执行完后,str2 和 str1 指向的同一片内存数据。当第三句执行时,Qt 会为 str2 的内部数据重新分配内存。这样做的好处是可以有效的减少大片数据拷贝的次数,提高程序的运行效率。

Qt 中隐式共享和写时拷贝就是利用 QSharedDataPointer 和 QSharedData 这两个类来实现的。

比如我们有个类叫做 Employee,里面有些数据希望能够利用隐式共享和写时拷贝技术。那么我们可以把需要隐式共享的数据封装到另一个类中,比如叫做 EmployeeData,这个类要继承自 QSharedData。

class EmployeeData : public QSharedData
  {
    public:
      EmployeeData() : id(-1) { }
      EmployeeData(const EmployeeData &other)
          : QSharedData(other), id(other.id), name(other.name) { }
      ~EmployeeData() { }

      int id;
      QString name;
  };

QWeakPointer

解除循环引用

QWeakPointer 是为配合 QSharedPointer 而引入的一种智能指针。而什么叫循环引用,就是说:两个对象互相使用一个 QSharedPointer成员变量指向对方(你中有我,我中有你)。由于QSharedPointer是一个强引用的计数型指针,只有当引用数为0时,就会自动删除指针释放内存,但是如果循环引用,就会导致QSharedPointer指针的引用永远都不能为0,这时候就会导致内存无法释放。
所以QWeakPointer诞生了,它就是为了打破这种循环的。并且,在需要的时候变成QSharedPointer,在其他时候不干扰QSharedPointer的引用计数。它没有重载 * 和 -> 运算符,因此不可以直接通过 QWeakPointer 访问对象,典型的用法是通过 lock() 成员函数来获得 QSharedPointer,进而使用对象。

QSharedPointer<Children> m_pChildren;

QSharedPointer<Parent> m_pParent;

------改为--------------------------
QWeakPointer<Children> m_pChildren;

QWeakPointer<Parent> m_pParent;

26.MinGW是什么

 MinGW 提供了一套简单方便的Windows下的基于GCC 程序开发环境。

27.connect()是前四个参数

1,sender - 发送信号的对象指针。
2,&SenderClass::signal - 指向发送者类中定义的信号。这里需要提供信号的名称和它的参数签名。使用 & 来获取成员信号的指针。
3,receiver - 接收信号的对象指针。
4,&ReceiverClass::slot - 指向接收者类中定义的槽函数。同样需要提供槽函数的名称和参数签名。使用 & 来获取成员槽的指针。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值