firemail
标题: Qt 面试指南(内含面试题) [打印本页]
作者: Qter 时间: 2024-2-20 18:14
标题: Qt 面试指南(内含面试题)
Qt 面试指南(内含面试题)
QT开发岗位需要掌握哪些技能?QT开发岗位需要掌握C++语言、QT框架、GUI编程、多线程编程等技能。此外,对于不同的岗位还需要掌握不同的领域知识,如网络编程、数据库编程等。
如何准备QT面试?首先,需要了解公司的招聘要求和岗位职责,了解面试流程和面试形式。其次,需要准备面试所需的知识和技能,可以通过阅读相关书籍、参加培训课程、做练习题等方式进行。最后,需要进行模拟面试,找到自己的不足之处并加以改进。
面试题:(需要pdf文档的可以进企鹅937552610裙嗱)
一、c++基础知识1、进程和线程的同步方式
进程:1)管道,是内核里的一串缓存
2)消息队列
3)共享内存
4)信号量机制
5)信号
6)socket
线程:1)等待通知机制
2)共享内存
3)管道
5)并发工具
信号量、读写锁、互斥锁和条件变量
线程的死锁概念 :线程间相互等待临界资源而造成彼此无法继续执行
方式一
1)创建一个线程类的子对象,继承QThread:
2)重写父类的run()方法,在该函数内部编写子线程要处理的具体业务流程
3)在主线程中创建子线程对象,new一个
4)启动子线程,调用start()方法
方式二
1)创建一个新的类,QObject派生
2)类中添加一个公有的自定义成员函数,函数体就是子线程中执行的业务逻辑
3)主线程中创建一个 QThread 对象,就是子线程对象
4)在主线程中创建工作的类对象,不要给创建的对象指定父对象
5)Mywork对象移动到创建的子线程对象中,需要调用QObject类提供的 moveToThread() 方法
注意事项:
业务对象, 构造的时候不能指定父对象
子线程中不能处理ui窗口(ui相关的类)
子线程中只能处理一些数据相关的操作, 不能涉及窗口
2、什么是堆栈
栈区(stack)
堆区(heap)抽象数据结构:后进先出
全局区(静态区)(static)
文字常量区
程序代码区
栈是自动分配释放,一级缓存,类似数组的结构。
堆是由程序员分配释放,二级缓存,速度慢些,先进后出。
3、常用的排序
插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序
冒泡排序:
时间复杂度:最好O(n),最坏O(n的2次方)。
空间复杂度:O(1);
4、数组和链表的区别
1)数组连续储存、固定长度、增删需要移动其他元素。链表动态分配,不连续,需要malloc或
new申请内存,不用时要free或delete.
2)数组访问效率高,链表删插效率高
3)数组利用下标定位,查找的时间复杂度是O(1),链表通过遍历定位元素,查找的时间复杂度是O(N)。
4)数组插入和删除需要移动其他元素,时间复杂度是O(N),链表的插入或删除不需要移动其他元素,时间复杂度是O(1)。
5、回调函数的三种典型使用场景:
1)实现函数功能重定义
2)扩展函数功能
3)实现程序分层设计
6、区分static和const
static :
(1) 修饰全局变量:变量只在本模块内可见,在定义不需要与其他文件共享的全局变量时,加上 static 关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用。
(2) 修饰局部变量:变量在全局数据区分配内存空间,编译器自动对其初始化,其作用域为局部作用域,当定义它的函数结束时,其作用域随之结束。
(3) 修饰函数:函数的使用方式与全局变量类似,在函数的返回类型前加上 static,就是静态函数,静态函数只能在声明它的文件中可见,其他文件不能引用该函数,不同的文件可以使用相同名字的静态函数,互不影响。
const :
被 const 修饰的变量是只读变量,本质还是变量,有人称其为常变量,和普通变量的区别在于常变量不能用于左值,其余的用法和普通常量一样,变量名不可以改变。
7、工作中有没有使用过动态库和静态库?能不能简单说下两者的区别?
答:静态库:在链接阶段将汇编生成的目标文件.o与引用库一起链接打包到可执行文件中,可简单看成(.o或者.obj文件的集合)。(1)对函数库的链接是放在编译时期完成的(2)程序在运行时与函数库没有瓜葛,移植方便(3)浪费空间和资源
动态库:(1)将库函数的链接载入推迟到程序运行时期(2)可以实现进程间的资源共享(因此也称为共享库)(3)将一些程序升级变得简单(4)可以真正的做到链接载入完全由程序员在程序代码中控制(显示调用)
8、虚函数:
父类型的指针指向其子类的实例
存在虚函数的类都有一个一维的虚函数表叫做虚表,
类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的。
默认构造函数、初始化构造函数、拷贝构造函数、移动构造函数
构造函数为什么不能被声明为虚函数?
虚函数对应一个vtale,这个表的地址是存储在对象的内存空间的。如果将构造函数设置为虚函数,
就需要到vtable 中调用,可是对象还没有实例化,没有内存空间分配,如何调用
9、STL相关知识
STL由6部分组成:容器(Container)、算法(Algorithm)、 迭代器(Iterator)、
仿函数(Function object)、适配器(Adaptor)、空间配制器(Allocator)
容器、算法、迭代器、仿函数、适配器、空间配置器
容器:数组(array) , 链表(list), tree(树),栈(stack), 队列(queue), 集合(set),映射表(map)
string容器
Vector容器是单向开口的连续内存空间,deque则是一种双向开口的连续线性空间
stack是一种先进后出
Queue是一种先进先出
list容器链表是一种物理存储单元上非连续、非顺序的存储结构
set/multiset二叉树
map/multimap二叉树
交换,查找,遍历,复制,修改,反转,排序,合并等
10、智能指针:auto_ptr(17后已遗弃)、unique_ptr、shared_ptr 和 weak_ptr
智能指针本质就是一个类模板,当智能指针对象使用完后,对象就会自动调用析构函数去释放该指针所指向的空间。
shared_ptr采用的是引用计数原理来实现多个shared_ptr对象之间共享资源:
shared_ptr在内部会维护着一份引用计数,用来记录该份资源被几个对象共享。
当一个shared_ptr对象被销毁时(调用析构函数),析构函数内就会将该计数减1。
如果引用计数减为0后,则表示自己是最后一个使用该资源的shared_ptr对象,必须释放资源。
如果引用计数不是0,就说明自己还有其他对象在使用,则不能释放该资源,否则其他对象就成为野指针。
11、指针和引用的区别
指针是一个实体,而引用仅是个别名。
引用只能在定义时被初始化一次,之后不可变,但指针可以改变。
指针可以有多级,但引用只有一级。
引用不能为空,指针可以为空。
sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小
指针和引用的自增(++)运算意义不一样
引用是类型安全的,而指针不是 ,引用比指针多了类型检查。
引用没有const,指针有const,const的指针不可变。
访问实体方式不同,指针需要显式解引用,引用编译器自己处理
【文章福利】Qt开发学习资料包、大厂面试题、技术视频和学习路线图,包括(Qt C++基础,数据库编程,Qt项目实战、Qt框架、QML、Opencv、qt线程等等)有需要的可以进企鹅裙937552610领取哦~
二、网络编程1、描述QT的TCP通讯流程
服务端:(QTcpServer)
①创建QTcpServer对象
②监听list需要的参数是地址和端口号
③当有新的客户端连接成功回发送newConnect信号
④在newConnection信号槽函数中,调用nextPendingConnection函数获取新连接QTcpSocket对象
⑤连接QTcpSocket对象的readRead信号
⑥在readRead信号的槽函数使用read接收数据
⑦调用write成员函数发送数据
客户端:(QTcpSocket)
①创建QTcpSocket对象
②当对象与Server连接成功时会发送connected 信号
③调用成员函数connectToHost连接服务器,需要的参数是地址和端口号
④connected信号的槽函数开启发送数据
⑤使用write发送数据,read接收数据
1)长连接的保活问题
标准TCP层协议里把对方超时设为2小时,若服务器端超过了2小时还没收到客户的信息,它就发送探测报文段,若发送了10个探测报文段(每一个相隔75S)还没有收到响应,就假定客户出了故障,并终止这个连接。因此应对tcp长连接进行保活。
2)TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;
解决方法二:发送固定长度的消息
解决方法三:把消息的尺寸与消息一块发送
解决方法四:双方约定每次传送的大小
解决方法五:双方约定使用特殊标记来区分消息间隔
解决方法六:标准协议按协议规则处理,如Sip协议
3)粘包问题
TCP产生粘包问题的主要原因是:TCP是面向连接的,所以在TCP看来,并没有把消息看成一条条的,而是全部消息在TCP眼里都是字节流,
因此A、B消息混在一起后,TCP就分不清了。
粘包问题的最本质原因在与接收对等方无法分辨消息与消息之间的边界在哪。我们通过使用某种方案给出边界,例如:
包头加上包体长度。包头是定长的4个字节,说明了包体的长度。接收对先接收包体长度,依据包体长度来接收包体。
解决方法一:TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;
解决方法二:发送固定长度的消息
解决方法三:把消息的尺寸与消息一块发送
解决方法四:双方约定每次传送的大小
解决方法五:双方约定使用特殊标记来区分消息间隔
解决方法六:标准协议按协议规则处理,如Sip协议
发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。
发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。
指定数据长度的解决方法,主要思路:
我们在数据结构中有个成员代表了长度(消息头),我们准备一个足够大的消息缓冲区(程序中是1024000个字节),循环使用socket中的recv每次读取最多102400个字节,然后把循环接收的消息拼接到消息缓冲区中,直到接收到的消息大于消息头指示的长度,则接收到了一个完整的消息(所以我们的消息缓冲区要比完整的消息还要大才行),进行消息处理。
4)TCP与UDP区别总结:
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保 证可靠交付
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
7、三次握手和四次挥手具体流程
SYN:请求建立连接,FIN:请求断开连接,ACK:确认是否有效, seq:序列号, ack:确认号
1)三次握手
1.客户端向服务端发送⼀个SYN=1(请求建立连接),并生成一个序列号seq=j。
2.服务端接收到SYN=1后,给客户端发送⼀个SYN=1与ACK=1;并将ack置为j+1;同时生成一个序列号seq=k。
3.客户端接收到会检查ack是否为j+1与ACK是否为1,如果是,则会给服务端发送一个ACK=1与ack=k+1,以及自己的序列号seq=j=1; 服务端接收到会检查ACK是否为1与ack是否为k+1,如果是则代表连接建立成功,两者间可以传递数据。
2)四次挥手
1.客户端向服务端发送FIN=1(请求关闭连接),并生成一个序列号seq=x。
2.服务端接收FIN后,向客户端发送ACK=1,ack=x+1,并生成序列号seq=y(客户端无数据发送,但服务器端需发送完最后的数据)。
3.服务端处理完所有数据后,向客户端发送FIN=1与ACK=1,ack=x+1,并生成序列号z,表示服务端现在可以断开连接。
4.客户端收到服务端的数据包后,会向服务端发送ACK=1,seq=x=1,ack=z+1(需要等待2MSL后才可断开连接)。
8、指针和引用的区别
指针是一个实体,而引用仅是个别名。
引用只能在定义时被初始化一次,之后不可变,但指针可以改变。
指针可以有多级,但引用只有一级。
引用不能为空,指针可以为空。
sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小
指针和引用的自增(++)运算意义不一样
引用是类型安全的,而指针不是 ,引用比指针多了类型检查。
引用没有const,指针有const,const的指针不可变。
访问实体方式不同,指针需要显式解引用,引用编译器自己处理
9、HTTP 是一种 超文本传输协议
因特网的协议栈由五个部分组成:物理层、链路层、网络层、运输层和应用层
URL(即网址)
浏览器会向DNS(域名服务器,后面会说)提供网址
HTML 称为超文本标记语言
http协议是应用层协议,主要是解决如何包装数据。而tcp协议是传输层协议,主要解决数据如何在网络中传输。
通俗点说,http的任务是与服务器交换信息,它不管怎么连到服务器和保证数据正确的事情。而tcp的任务是保证连接的可靠,它只管连接,它不管连接后要传什么数据。
http协议是建立在tcp之上的,http是无状态的短链接,而tcp是有状态的长链接。
HTTPS:是以安全为目标的 HTTP 通道,是 HTTP 的安全版。HTTPS 的安全基础是 SSL。
SSL 协议位于 TCP/IP 协议与各种应用层协议之间,为数据通讯提供安全支持。
二、HTTP 与 HTTPS 的区别
1、HTTPS 协议需要到 CA (Certificate Authority,证书颁发机构)申请证书,一般免费证书较少,因而需要一定费用。
(以前的网易官网是http,而网易邮箱是 https 。)
2、HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的 SSL 加密传输协议。
3、HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、HTTP 的连接很简单,是无状态的。HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,
比 HTTP 协议安全。(无状态的意思是其数据包的发送、传输和接收都是相互独立的。
无连接的意思是指通信双方都不长久的维持对方的任何信息。)
三、QT相关知识1、什么是元对象系统
元对象系统是一个基于标准C++的扩展,为QT提供了信号与槽机制、实时类型信息、动态属性系统。
元对象系统的三个基本条件:类必须继承自QObject、类声明Q_OBJECT宏(默认私有)、元对象编译器moc。
信号与槽类似观察者模式;
回调函数的本质是基于“你想让别人的代码执行你的代码,而别人的代码你又不能动”这种产生的;
对象树;
信号与槽的实现是借助了Qt 的元对象系统,元对象系统有一个元对象编译器,
程序编译之前会有一个预处理过程,预处理将一个类/对象中的信号,槽的字符串值分别保存在一个容器中,可能是字符串或者其他的有序容器
第5个参数跟线程相关Qt::AutoConnection
1)Qt信号槽机制的优势
(1)类型安全。需要关联的信号和槽的签名必须是等同的,
(2)松散耦合。
(3)信号和槽机制增强了对象间通信的灵活性。一个信号可以关联多个槽,也可以多个信号关联一个槽。
2)Qt信号槽机制的不足
同回调函数相比,信号和槽机制运行速度有些慢。通过传递一个信号来调用槽函数将会比直接调用非虚函数运行速度慢10倍。原因如下:
(1)需要定位接收信号的对象;
(2)安全地遍历所有的关联(如一个信号关联多个槽的情况);
(3)编组/解组传递的参数;
(4)多线程的时候,信号可能需要排队等待。
然而,与创建对象的new操作及删除对象的delete操作相比,信号和槽的运行代价只是他们很少的一部分。信号和槽机制导致的这点性能损耗,对实时应用程序是可以忽略的。
知道QT事件机制有几种级别的事件过滤吗?能大致描述下吗?
答:根据对Qt事件机制的分析, 我们可以得到5种级别的事件过滤,处理办法. 以功能从弱到强, 排列如下:
1)重载特定事件处理函数.
最常见的事件处理办法就是重载象mousePressEvent(), keyPressEvent(), paintEvent() 这样的特定事件处理函数.
2)重载event()函数.
通过重载event()函数,我们可以在事件被特定的事件处理函数处理之前(象keyPressEvent())处理它. 比如, 当我们想改变tab键的默认动作时,一般要重载这个函数. 在处理一些不常见的事件(比如ayoutDirectionChange)时,evnet()也很有用,因为这些函数没有相应的特定事件处理函数. 当我们重载event()函数时, 需要调用父类的event()函数来处理我们不需要处理或是不清楚如何处理的事件. 3) 在Qt对象上安装事件过滤器.
安装事件过滤器有两个步骤: (假设要用A来监视过滤B的事件)
首先调用B的installEventFilter( const QOject *obj ), 以A的指针作为参数. 这样所有发往B的事件都将先由A的eventFilter()处理.
然后, A要重载QObject::eventFilter()函数, 在eventFilter() 中书写对事件进行处理的代码.
4) 给QAppliction对象安装事件过滤器.
一旦我们给qApp(每个程序中唯一的QApplication对象)装上过滤器,那么所有的事件在发往任何其他的过滤器时,都要先经过当前这个 eventFilter(). 在debug的时候,这个办法就非常有用, 也常常被用来处理失效了的widget的鼠标事件,通常这些事件会被QApplication::notify()丢掉. ( 在QApplication::notify() 中, 是先调用qApp的过滤器, 再对事件进行分析, 以决定是否合并或丢弃)
5) 继承QApplication类,并重载notify()函数.
Qt 是用QApplication::notify()函数来分发事件的.想要在任何事件过滤器查看任何事件之前先得到这些事件,重载这个函数是唯一的办法. 通常来说事件过滤器更好用一些, 因为不需要去继承QApplication类. 而且可以给QApplication对象安装任意个数的事件。
自定义界面
UI设计器集成了Qt样式表的编辑功能
前景色color
背景色background-color
选中后颜色selection-color
背景图片background-image
选择器PushButton 、QDialog QPushButton、QPushButton#btnOk 子控件:drop-down、up-button、down-button
伪状态:hover(鼠标移动到条目上方时),active(处于活动的窗体)
属性:min_width、padding-top、border-width、
1、使用Qt Designer
2、qApp->setStyleSheet(“QLineEdit{ background-color: gray}”);
自定义QT控件
1、自定义Widget子类QmyBattery
paintEvent()事件
QmyBattery继承于QWidget
Q_UNUSED(event)
QPainter/QRect/QColor
提升法
2、自定义Qt Designer插件
编译器用MSVC2015 32bit
Q_INTERFACES声明了实现接口
Q_PLUGIN_METADATA声明了元数据名称
要把dll和lib放到插件目录下
D:\Qt\Qt5.9.1\Tools\QtCreator\bin\plugins\designer
D:\Qt\Qt5.9.1\5.9.1\msvc2015\plugins\designer
QWT,是一个基于LGPL版权协议的开源项目, 可生成各种统计图
QT和MFC消息机制比较:
mfc的消息机制其实就是消息映射机制,程序员需要将自定义消息和对应的处理函数添加到消息映射表中。通过PostMessage和SendMessage来实现异步和同步消息。
QT的信号槽机制是信号和槽函数通过QObject::connect动态链接上后存储到元对象系统中,通过emit发送信号,对应的槽函数执行。
比较
Qt的信号槽是动态链接的,而MFC的消息映射是静态的
Qt的信号支持自定义参数,且类型安全
在多线程中,MFC需要向已知线程对象发布消息,而Qt可以不考虑多线程之间的信号槽关系
总结
Qt相比较MFC的消息机制,使用起来更方便,最大的优势是Qt支持动态链接信号槽。
四、项目相关1、定位问题:
请问下,如果软件除了问题(Bug),如何快速定位?主要方法有哪些?
答:打印输出/代码调试/日志记录/分析工具/找同事讨论。
1)二分法定位技巧
无论是有多复杂的代码,利用二分法定位技巧一般都是可以定位到问题所在。
从二分法定位技巧可以延伸出一些具体的处理bug的方法,比如:对输入数据二分、对代码版本二分、注释掉部分代码、在不同位置插入试探性代码、对运行环境二分。
2)IDE调试
IDE的VS debug的功能简直就是立竿见影。它可以加断点,单步调试。
单步调试可以让我们对代码逻辑,执行顺序,以及各种中间结果更加清晰。
至于本身容易出错的BUG,用IDE调试简直是再合适不过了。
3)重新读一遍程序
相对新手程序员来说,如果代码出现bug,可以重新读一遍程序。这种方法是最有效、最快速的 Debug 方式。
4)必杀,重写一遍
如果你发现无论如何也找不到BUG,而且代码只是复杂,本身不是很长,直接重写代码吧!
5)小黄鸭调试法
小黄鸭调试法是程序员们经常使用的调试代码方法之一。
小黄鸭不懂程序,所以我们可以向他解释每一行程序的作用,以此来激发灵感。
内存泄露及解决办法:
什么是内存泄露?
简单地说就是申请了一块内存空间,使用完毕后没有释放掉。(1)new和malloc申请资源使用后,没有用delete和free释放;(2)子类继承父类时,父类析构函数不是虚函数。(3)Windows句柄资源使用后没有释放。
怎么检测?
第一:良好的编码习惯,使用了内存分配的函数,一旦使用完毕,要记得使用其相应的函数释放掉。
第二:将分配的内存的指针以链表的形式自行管理,使用完毕之后从链表中删除,程序结束时可检查改链表。
第三:使用智能指针。
第四:一些常见的工具插件,如ccmalloc、Dmalloc、Leaky、Valgrind等等。
数据库相关
什么是事务
并发控制
原子性、一致性、隔离性、持续性
valueChanged(int)
QMutex mutex;
QMutexLocker locker(&mutex);
waitForReadyRead()
常用的设计结构:
工厂方法 Factory Method 定义了创建对象的接口,让子类决定实例化哪个类
单例 Singleton 确保一个类只有一个实例,并提供一个访问它的全局访问点
原型 Prototype 通过拷贝原型对象创建新的对象。
适配器 Adapter 将一个类的接口转换成希望的另外一个接口,使得原本不兼容的接口可以协同工作。
观察者 Observer 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
单例:懒汉模式和饿汉模式
饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了;线程安全。
而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例;
线程不安全,就只有在实例化之前调用的时候加锁,后面不加锁。
C++新特性主要包括包含语法改进和标准库扩充两个方面,主要包括以下11点:
语法的改进
(1)统一的初始化方法,允许构造函数或其他函数像参数一样使用初始化列表
(2)成员变量默认初始化
(3)类型推导 auto关键字 用于定义变量,编译器可以自动判断的类型(前提:定义一个变量时对其进行初始化)
(4)decltype 求表达式的类型
(5)智能指针 shared_ptr
(6)空指针 nullptr(原来NULL)
nullptr表示空指针,是一个关键词
NULL是老版本的,是一个0的宏
(7)基于范围的for循环
(8)右值引用和move语义 让程序员有意识减少进行深拷贝操作
标准库扩充(往STL里新加进一些模板类,比较好用)
(9)无序容器(哈希表) 用法和功能同map一模一样,区别在于哈希表的效率更高
(10)正则表达式 可以认为正则表达式实质上是一个字符串,该字符串描述了一种特定模式的字符串
(11)Lambda表达式
https://zhuanlan.zhihu.com/p/671808272
作者: Qter 时间: 2024-2-21 13:33
本帖最后由 Qter 于 2024-2-21 13:36 编辑
https://www.cnblogs.com/unicornsir/p/16826261.html
1.connect几个类型的区别Qt::ConnectionType:Qt不同类型connect的详细区别说明与应用
一、介绍
1.Qt::AutoConnection
默认连接类型,如果信号接收方与发送方在同一个线程,则使用Qt:irectConnection,否则使用Qt:ueuedConnection;连接类型在信号发射时决定。
2.Qt:irectConnection
信号所连接至的槽函数将会被立即执行,并且是在发射信号的线程;倘若槽函数执行的是耗时操作、信号由UI线程发射,则会阻塞Qt的事件循环,UI会进入无响应状态
3.Qt:ueuedConnection
槽函数将会在接收者的线程被执行,此种连接类型下的信号倘若被多次触发、相应的槽函数会在接收者的线程里被顺次执行相应次数;当使用QueuedConnection时,参数类型必须是Qt基本类型,或者使用qRegisterMetaType() 进行注册了的自定义类型
4.Qt::BlockingQueuedConnection
和Qt:ueuedConnection类似,区别在于发送信号的线程在槽函数执行完毕之前一直处于阻塞状态;收发双方必须不在同一线程,否则会导致死锁
5.Qt::UniqueConnection
可以搭配以上所有连接类型使用,一经设置之后,同一信号与同一槽函数的二次连接将会失败
二、场景
多线程中同一对象的不同槽函数,某个槽函数的执行依赖另一个槽函数的执行结果;可以将前一个槽函数的连接类型设置成DirectConnection,确保调用之后能被立即执行 三、测试1.声明[backcolor=rgb(255, 255, 255) !important][size=1em]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| #ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QThread>
#include <QPushButton>
#include <windows.h>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class ThreadWorker : public QObject
{
Q_OBJECT
public:
ThreadWorker(QObject* parent);
public slots:
void queuedConnect();
void directConnect();
};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
QVector<QThread*> listThread;
QVector<ThreadWorker*> listWorker;
Ui::Widget *ui;
private slots:
void on_butDirect_clicked();
void on_butQueued_clicked();
void on_butAuto_clicked();
signals:
void directConnect();
void autoConnect();
void queuedConnect();
};
#endif // WIDGET_H
|
2.实现[backcolor=rgb(255, 255, 255) !important][size=1em]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
| #include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
for(int i = 0 ; i < 3 ; i++)
{
listThread.append(new QThread(this));
listWorker.append(new ThreadWorker(nullptr));
}
connect(this,SIGNAL(directConnect()),listWorker[0],SLOT(directConnect()),Qt:irectConnection);
connect(this,SIGNAL(autoConnect()),listWorker[2],SLOT(directConnect()),Qt::AutoConnection);
connect(this,SIGNAL(queuedConnect()),listWorker[1],SLOT(queuedConnect()),Qt:ueuedConnection);
connect(this,SIGNAL(directConnect()),listWorker[1],SLOT(directConnect()),Qt:ueuedConnection);
for(int i = 0 ; i < 3 ; i++ )
listWorker->moveToThread(listThread);
for(auto i : listThread) i->start();
qDebug()<<"The main thread id is "<< QThread::currentThreadId();
}
Widget::~Widget()
{
delete ui;
for(auto i : listThread)
i->exit();
}
void Widget:n_butDirect_clicked()
{
emit directConnect();
}
void Widget:n_butQueued_clicked()
{
emit queuedConnect();
}
void Widget:n_butAuto_clicked()
{
emit autoConnect();
}
ThreadWorker::ThreadWorker(QObject *parent) : QObject(parent)
{
}
void ThreadWorker::queuedConnect()
{
for(int i = 0 ; i <10 ; i ++)
Sleep(100);
qDebug()<<"The kid thread id is "<< QThread::currentThreadId();
}
void ThreadWorker::directConnect()
{
for(int i = 0 ; i <10 ; i ++)
Sleep(100);
qDebug()<<"The kid thread id is "<< QThread::currentThreadId();
}
|
四、结论同一对象的不同槽函数可以有不同的连接类型,槽函数在哪个线程被执行由连接类型、信号发射时收发双方是否在一个线程决定
Qt面试题:
1.connect几个类型的区别
2.自绘控件
3.Qt常用线程几种方式,数据竞争,加锁同步等等
4.Model-View-Delegate的理解
5.Qt的插件机制是怎么做的
6.信号槽机制是怎么实现的
7.对Qt事件循环的理解
8.Qt的事件分发是怎么做的
9.QMetaObject::invokeMethod
10.元对象系统,信号槽连接方式,事件分发拦截,继承QWidget样式自绘
11.GraphicsView会问一下,几个坐标系,还有怎么用Gpahicsview绘制一个五角星
12.qt实现发布订阅的设计方法
Qt-Pub-Sub: 基于Qt的发布订阅机制
Quick Event: 基于QT设计的一套控制与界面完全分离的代码模型
Qt实战7.轻量级发布订阅模式 - Qt小罗 - 博客园
13.通过c++11的std::bind及std::function实现类方法回调,模拟Qt实现信号槽
使用 C++11 编写类似 QT 的信号槽
https://github.com/chxuan/cpp-utils/tree/master/Connect
Qt事件
1、事件循环一般用exec()函数开启。QApplicaion::exec()、QMessageBox::exec()都是事件循环。其中前者又被称为主事件循环。
事件循环首先是一个无限“循环”,程序在exec()里面无限循环,能让跟在exec()后面的代码得不到运行机会,直至程序从exec()跳出。从exec()跳出时,事件循环即被终止。QEventLoop::quit()能够终止事件循环。
2、事件循环是可以嵌套的,一层套一层,子层的事件循环执行exec()的时候,父层事件循环就处于中断状态;当子层事件循环跳出exec()后,父层事件循环才能继续循环下去。
Qt的事件处理有5中级别:
1.重写控件的事件处理函数:如重写keyPressEvent(),mousePressEvent()和paintEvent(),这是最常用的事件处理方法,我们已经看到过很多这样的例子了。
2.重写QObject::event(),在事件到达事件处理函数时处理它。在需要改变Tab键的惯用法时这样做。也可以处理那些没有特定事件处理函数的比较少见的事件类型(例如,QEvent::HoverEnter)。我们重写event()时,必须要调用基类的event(),由基类处理我们不需要处理的那些情况。
3.给QObject对象安装事件过滤器:对象用installEventFilter()后,所有达到目标控件的事件都首先到达监视对象的eventFilter()函数。如果一个对象有多个事件过滤器,过滤器按顺序激活,先到达最近安装的监视对象,最后到达最先安装的监视对象。
4.给QApplication安装事件过滤器,如果qApp(唯一的QApplication对象)安装了事件过滤器,程序中所有对象的事件都要送到eventFilter()函数中。这个方法在调试的时候非常有用,在处理非活动状态控件的鼠标事件时这个方法也很常用。
5.继承QApplication,重写notify()。Qt调用QApplication::nofity()来发送事件。重写这个函数是在其他事件过滤器处理事件前得到所有事件的唯一方法。通常事件过滤器是最有用的,因为在同一时间,可以有任意数量的事件过滤器,但是notify()函数只有一个。
许多事件类型,包括鼠标,键盘事件,是能够传播的。如果事件在到达目标对象的途中或者由目标对象处理掉,事件处理的过程会重新开始,不同的是这时的目标对象是原目标对象的父控件。这样从父控件再到父控件,知道有控件处理这个事件或者到达了最顶级的那个控件。
C++面试题:
1. new malloc使用的区别
2. static的用途,区别
3. 智能指针相关的,有哪些,区别是什么
4. 多态的实现机制,虚函数表是怎样的
5. 多态有没有别的实现方案
6. 线程,竞争,同步(更偏向于标准库)
7. C++11都有哪些东西,用的咋样
8. 标准库里的容器常用哪些,区别是什么,实现原理
9. 析构函数、构造函数能不能虚拟的,为什么
10. 右值引用了解多少,怎么用
11. 模板用的怎么样,会不会写
12.C++可能会问智能指针/虚函数/面向对象之类的吧?
13.C++可能会问内存模型,线程安全,高速缓存
14.写个单例模式
15.写个斐波那契数列
int Fib(int n)
{
if (n == 1 || n == 2)
return 1;
else
return Fib(n - 1) + Fib(n - 2);
}
16.卷数据结构和算法
(1)编码实现字符串转化为数字,例如“24145”-->24145
(2)单链表逆序
(3)冒泡排序,快速排序
2.C++-选择排序、冒泡排序、插入排序、希尔排序、归并排序、快速排序_诺谦的博客-CSDN博客
https://github.com/huihut/interview
其他:
A
1. null指针调用某个类的方法,会怎样
2. 大小端的是什么意思
3. 结构体、类成员的字节对齐
4. 大小端相关的强制类型转换访问问题
5. 类继承问题,B继承A,B什么都没有新增,能不能互相强制转换
6. 私有成员如何访问,除了friend这些
7. 网络传输,json太臃肿,怎么提速。(猜还是直接内存字节传输,约定大小端这种)
8. 多线程、进程、网络,实际去抽象应该要解决一个什么问题(猜是同步,面试官没说)
9. 提了项目的多进程通信用的管道没用socket,说代理问题,但是面试官比较怀疑会有代理问题
B
1、const、static作用。
2、c++面向对象三大特征及对他们的理解,引出多态实现原理、动态绑定、菱形继承。
3、虚析构的必要性,引出内存泄漏,虚函数和普通成员函数的储存位置,虚函数表、虚函数表指针。
4、malloc、free和new、delete区别,引出malloc申请大内存、malloc申请空间失败怎么办。
5、stl熟悉吗,vector、map、list、hashMap,vector底层,map引出红黑树。优先队列用过吗,使用的场景。无锁队列听说过吗,原理是什么(比较并交换)
6、实现擅长的排序,说出原理(快排、堆排)
7、四种cast,智能指针。static_cast, const_cast, reinterpret_cast, dynamic_cast。
8、tcp和udp区别
9、进程和线程区别。
10、指针和引用作用以及区别。
11、c++11用过哪些特性,auto作为返回值和模板一起怎么用,函数指针能和auto混用吗。
12、boost用过哪些类,thread、asio、signal、bind、function
13、单例、工厂模式、代理、适配器、模板,使用场景。
14、QT信号槽实现机制,QT内存管理,MFC消息机制。
15、进程间通信。会选一个详细问。
16、多线程,锁和信号量,互斥和同步。
17、动态库和静态库的区别。
18、c++一般情况下复制对象,都要考虑深拷贝,比如char *成员要申请空间,并拷贝源对象的内存。假设函数返回的临时对象,不可避免有一次拷贝,但实际临时对象会立刻释放掉,此时直接将管理的资源交给新对象即可,不需要重新申请。可以写拷贝构造函数,也可以借助c++11移动构造函数move。
C
1、统计字符串中出现次数最多的字母并输出
2、上亿行数据的查询 c++
3、C++查找文本中出现最频繁的10个单词
D
1、map和unordered_map区别及其优缺点
map和unordered_map区别及其优缺点 - 己平事 - 博客园
2、map和unordered_map的key可以是什么数据类型?能否是c++类?
C++ map的键类型可以是一个类,比如键类型可以是C++标准库中的string类,
但是对作为键的类有一个约束,那就是这个类必须定义小于操作符,也就是要重载小于运算操作符。
C++标准库的string类就定义了小于操作符。
3、map和multimap的区别
map支持唯一键值,每个键只能出现一次;而multimap中相同键可以出现多次。multimap不支持[]操作符。
4、c++11 share指针,shared_ptr,什么时候引用计数-1?是否一定是内存安全?
当复制或拷贝时,引用计数加1,当智能指针析构时,引用计数减1,如果计数为零,代表已经没有指针指向这块内存,那么我们就释放它。
主动释放对象
shared_ptrr<int> up1(new int(10));
up1 = nullptr ;//int(10) 的引用计数减1,计数归零内存释放
或up1 = NULL; //作用同上
重置
up.reset() ; //将p重置为空指针,所管理对象引用计数 减1
up.reset(p1); //将p重置为p1(的值),p 管控的对象计数减1,p接管对p1指针的管控
up.reset(p1,d); //将p重置为p(的值),p 管控的对象计数减1并使用d作为删除器
交换
std::swap(p1,p2); //交换p1 和p2 管理的对象,原对象的引用计数不变
p1.swap(p2); //同上
5、c++11的泛型,有哪些了解?
6、c++联合体,哪些数据类型可以?类行不行?
class Hello
{
Hello()
{
std::cout << "hello" << std::endl;
}
~Hello()
{
}
};
class World
{
World()
{
std::cout << "world" << std::endl;
}
~World()
{
}
};
//共用体(union)是一种数据格式,它能够存储不同的数据类型,但只能同时存储其中的一种类型。
//Union的一大特征在于,一个Union类中的所有数据共享同一段内存。
//如果union类的成员包含自己的构造函数,析构函数,那么同一Union类的成员在初始化时,就有可能会执行不同的构造函数。
//这是无法预料的。所以,我们在定义Union类时要尽量避免成员变量是对象(含有自己的构造函数)
union MyUnion
{
int iA;
double dA;
char cA;
Hello j;
World r;
std::string strChar;
};
7、vector和list的迭代器怎么设计和实现?vector中迭代器的实现
C++四种类型转换
转换类型操作符 作用
const_cast 去掉类型的const或volatile属性
static_cast 无条件转换,静态类型转换
dynamic_cast 有条件转换,动态类型转换,运行时检查类型安全(转换失败返回NULL)
reinterpret_cast 仅重新解释类型,但没有进行二进制的转换
dynamic_cast
//有条件转换,动态类型转换
//1) 安全的基类和子类之间的转换
//2) 必须有虚函数
//3) 相同基类不同子类之间的交叉转换,但结果返回NULL
reinterpret_cast
//仅重新解释类型,但没有进行二进制的转换:
//1) 转换的类型必须是一个指针、应用、算数类型、函数指针或者成员指针
//2) 在比特级别上进行转换,可以把一个指针转换成一个整数,也可以把一个整数
//转换成一个指针,(先把一个指针转换成一个整数,再把该整数转换成原类型的指针,
//还可以得到原先的指针值)。但不能将非32bit的实例转成指针
//3) 最普通的用途就是在函数指针类型之间进行转换。
//4) 很难保证移植性
总结
去const属性用const_cast
基本类型转换用static_cast
多态类之间的类型转换用dynamic_cast
不同类型的指针类型转换用reinterpret_cast
C++单例
单例模式为什么双重校验锁?
双重校验锁第一次进行判空原因:当程序顺序执行的时候,如果不进行判空,每一个线程都会先去获得当前类的类锁,而其他线程都进入阻塞状态。单例模式中初始化单例的程序只会执行一次,大部分情况下会直接到return语句返回,如果都阻塞在获取锁的位置,会大大降低程序的运行速度。
双重校验锁第二次进行判空原因:假设有两个线程A和B,都进行完第一次判空了,A和B都阻塞在此。这个时候A线程获取了类锁,然后B线程被阻塞,A线程新建了一个实例后释放了锁,B线程获取锁,又新建了一个实例,这破坏了单例设计模式的初衷。
#include <iostream>
#include <memory>
#include <mutex>
class Singleton {
public:
static Singleton& GetInstance() {
static std:nce_flag s_flag;
std::call_once(s_flag, [&]() {
instance_.reset(new Singleton);
});
return *instance_;
}
~Singleton() = default;
void PrintAddress() const {
std::cout << this << std::endl;
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() = default;
private:
static std::unique_ptr<Singleton> instance_;
};
std::unique_ptr<Singleton> Singleton::instance_;
int main() {
Singleton& s1 = Singleton::GetInstance();
s1.PrintAddress();
Singleton& s2 = Singleton::GetInstance();
s2.PrintAddress();
return 0;
} 构造函数能否为虚函数?
构造函数不能是虚函数,因为虚函数是基于对象的,构造函数是用来产生对象的,若构造函数是虚函数,则需要对象来调用,但是此时构造函数没有执行,就没有对象存在,产生矛盾,所以构造函数不能是虚函数。
从存储空间角度,虚函数相应一个指向vtable虚函数表的指针,这大家都知道,但是这个指向vtable的指针事实上是存储在对象的内存空间的。问题出来了,假设构造函数是虚的,就须要通过 vtable来调用,但是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。
重点关注张小方的回复
欢迎光临 firemail (http://firemail.wang:8088/) |
Powered by Discuz! X3 |