Qt&C++面试题

Qt

自定义控件

应该做过吧?能举几个例子吗?还有其他的吗?

你觉得自定义控件的方法主要是哪些?

答:从外观设计上:QSS、继承绘制函数重绘、继承QStyle相关类重绘、组合拼装等等

从功能行为上:重写事件函数、添加或者修改信号和槽等等

QSS

QSS平时使用的多吗?能举几个例子吗?

都是如何使用,能说说吗?

答:1.将QSS统一写在一个文件中,通过程序给主窗口加载;

2.写成一个字符串中,通过程序给主窗口加载;

3.需要使用的地方,写一个字符串,加载给对象;

4.QT Designer中填写;

事件机制:

QT程序是事件驱动的,事件到处都可以遇到。能说说平时经常使用到哪些事件吗?

常见的QT事件类型如下:

键盘事件: 按键按下和松开 鼠标事件: 鼠标移动,鼠标按键的按下和松开

拖放事件: 用鼠标进行拖放 滚轮事件: 鼠标滚轮滚动

绘屏事件: 重绘屏幕的某些部分 定时事件: 定时器到时

焦点事件: 键盘焦点移动 进入和离开事件: 鼠标移入widget之内,或是移出

移动事件: widget的位置改变 大小改变事件: widget的大小改变

显示和隐藏事件: widget显示和隐藏 窗口事件: 窗口是否为当前窗口

知道QT事件机制有几种级别的事件过滤吗?能大致描述下吗?

答:根据对Qt事件机制的分析, 我们可以得到5种级别的事件过滤,处理办法. 以功能从弱到强, 排列如下:

1)重载特定事件处理函数.

最常见的事件处理办法就是重载象mousePressEvent(), keyPressEvent(), paintEvent() 这样的特定事件处理函数.

2)重载event()函数.

通过重载event()函数,我们可以在事件被特定的事件处理函数处理之前(象keyPressEvent())处理它. 比如, 当我们想改变tab键的默认动作时,一般要重载这个函数. 在处理一些不常见的事件(比如:LayoutDirectionChange)时,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对象安装任意个数的事件。

QT版本:

请问使用的QT版本是?有没有使用过QT4?QT5的信号槽与QT4相比有什么改进?

答:

  • 编译期:检查信号与槽是否存在,参数类型检查,Q_OBJECT是否存在

  • 信号可以和普通的函数、类的普通成员函数、lambda函数连接(而不再局限于信号函数和槽函数)

  • 参数可以是 typedef 的或使用不同的namespace specifier

  • 可以允许一些自动的类型转换(即信号和槽参数类型不必完全匹配)

信号槽机制:

能说下你的理解吗?

能用什么方法替代?槽函数可以是虚函数吗?

答:回调函数。可以。

信号槽同步与异步:

信号槽是同步的还是异步的?分别如何实现?

答:通常使用的connect,实际上最后一个参数使用的是Qt::AutoConnection类型:Qt支持6种连接方式,其中3中最主要:

1.Qt::DirectConnection(直连方式)(信号与槽函数关系类似于函数调用,同步执行)

当信号发出后,相应的槽函数将立即被调用。emit语句后的代码将在所有槽函数执行完毕后被执行。

2.Qt::QueuedConnection(排队方式)(此时信号被塞到信号队列里了,信号与槽函数关系类似于消息通信,异步执行)

当信号发出后,排队到信号队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,调用相应的槽函数。emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕。

3.Qt::AutoConnection(自动方式)

Qt的默认连接方式,如果信号的发出和接收这个信号的对象同属一个线程,那个工作方式与直连方式相同;否则工作方式与排队方式相同。

4.Qt::BlockingQueuedConnection(信号和槽必须在不同的线程中,否则就产生死锁)

这个是完全同步队列只有槽线程执行完成才会返回,否则发送线程也会一直等待,相当于是不同的线程可以同步起来执行。

5.Qt::UniqueConnection

与默认工作方式相同,只是不能重复连接相同的信号和槽,因为如果重复连接就会导致一个信号发出,对应槽函数就会执行多次。

6.Qt::AutoCompatConnection

是为了连接Qt4与Qt3的信号槽机制兼容方式,工作方式与Qt::AutoConnection一样。

如果这个参数不设置的话,默认表示的是那种方式呢?

没加的话与直连方式相同:当信号发出后,相应的槽函数将立即被调用。emit语句后的代码将在所有槽函数执行完毕后被执行。在这个线程内是顺序执行、同步的,但是与其它线程之间肯定是异步的了。如果使用多线程,仍然需要手动同步。

多线程:

多线程使用的多吗?能简单说说吗?QT多线程有两种方法实现。

知道死锁吗?死锁是如何产生的?

答:死锁的产生有如下四个必要条件

1. 资源是互斥的,同一时刻只能有一个进程占有该资源

2. 资源的释放只能有该进程自己完成

3. 线程在获取到需要资源之前,不会释放已有资源

4. 存在这么一条循环等待的队列,线程T1,T2,T3…, Tn

T1持有自己的资源请求T2的资源,….Tn持有自己的资源请求T1的资源

线程同步的方法有哪些?

答:

  1. 互斥量(QMutex)

​ QMutex m_Mutex; m_Mutex.lock(); m_Mutex.unlock();

  1. 互斥锁(QMutexLocker)

​ QMutexLocker mutexLocker(&m_Mutex);

​ 从声明处开始(在构造函数中加锁),出了作用域自动解锁(在析构函数中解锁)。

  1. 等待条件(QWaitCondition)

QWaitCondtion m_WaitCondition; m_WaitConditon.wait(&m_muxtex, time);

m_WaitCondition.wakeAll();

  1. QReadWriteLock类

》一个线程试图对一个加了读锁的互斥量进行上读锁,允许;

》一个线程试图对一个加了读锁的互斥量进行上写锁,阻塞;

》一个线程试图对一个加了写锁的互斥量进行上读锁,阻塞;、

》一个线程试图对一个加了写锁的互斥量进行上写锁,阻塞。

读写锁比较适用的情况是:需要多次对共享的数据进行读操作的阅读线程。

QReadWriterLock 与QMutex相似,除了它对 “read”,”write”访问进行区别对待。它使得多个读者可以共时访问数据。使用QReadWriteLock而不是QMutex,可以使得多线程程序更具有并发性。

  1. 信号量QSemaphore

但是还有些互斥量(资源)的数量并不止一个,比如一个电脑安装了2个打印机,我已经申请了一个,但是我不能霸占这两个,你来访问的时候如果发现还有空闲的仍然可以申请到的。于是这个互斥量可以分为两部分,已使用和未使用。

  1. QReadLocker便利类和QWriteLocker便利类对QReadWriteLock进行加解锁

设计模式:

设计模式平时有使用到吗?能不能说下常见的设计模式有哪些?能不能说说大致的概念?能不能具体说下工作中如何使用的?

答:总体来说设计模式分为三大类:

创建型模式,共五种工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

动态库

工作中有没有使用过动态库和静态库?能不能简单说下两者的区别?

答:静态库:在链接阶段将汇编生成的目标文件.o与引用库一起链接打包到可执行文件中,可简单看成(.o或者.obj文件的集合)。(1)对函数库的链接是放在编译时期完成的(2)程序在运行时与函数库没有瓜葛,移植方便(3)浪费空间和资源

动态库:(1)将库函数的链接载入推迟到程序运行时期(2)可以实现进程间的资源共享(因此也称为共享库)(3)将一些程序升级变得简单(4)可以真正的做到链接载入完全由程序员在程序代码中控制(显示调用)

动态库一般也会有个lib文件,那么和静态库lib文件有什么区别?

动态库中的.lib文件叫做导入库,对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。

静态库中的.lib叫做静态库,本身就包含了实际执行代码、符号表等等

TCP/UDP/HTTP(S):

TCP/UDP有使用过吗?能结合工作具体说说吗?

HTTP协议有使用过吗?QT5中使用的相关联的主要的几个类?

答:QNetworkAccessManager/QNetworkRequest/QNetworkReply。

算法:

平时使用算法比较多吗?能简单说下快排的思想吗?时间复杂度是多少?

答:基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

时间复杂度:平均为O(nlogn),最好为O(nlogn),最差为O(logn2)

数据库

工作中有使用数据库吗?MySql使用多吗?

串口通讯:

有使用到串口通讯吗?能简单说下具体应用吗?

SVN/Git:

平时团队或者公司是如何管理代码的?使用什么工具?

定位问题:

请问下,如果软件除了问题(Bug),如何快速定位?主要方法有哪些?

答:打印输出/代码调试/日志记录/分析工具/找同事讨论。

1、二分法定位技巧

无论是有多复杂的代码,利用二分法定位技巧一般都是可以定位到问题所在。

从二分法定位技巧可以延伸出一些具体的处理bug的方法,比如:对输入数据二分、对代码版本二分、注释掉部分代码、在不同位置插入试探性代码、对运行环境二分。

2、IDE调试

IDE的VS debug的功能简直就是立竿见影。它可以加断点,单步调试。

单步调试可以让我们对代码逻辑,执行顺序,以及各种中间结果更加清晰。

至于本身容易出错的BUG,用IDE调试简直是再合适不过了。

3、重新读一遍程序

相对新手程序员来说,如果代码出现bug,可以重新读一遍程序。这种方法是最有效、最快速的 Debug 方式。

4、必杀,重写一遍

如果你发现无论如何也找不到BUG,而且代码只是复杂,本身不是很长,直接重写代码吧!

5、小黄鸭调试法

小黄鸭调试法是程序员们经常使用的调试代码方法之一。

小黄鸭不懂程序,所以我们可以向他解释每一行程序的作用,以此来激发灵感。

IDE(集成开发环境):

Windows系统主流IDE:QT Creator、Visual Studio等

Mac系统主流:XCode等

问题:平时主要使用的IDE是什么?有没有做过QT跨平台?

引用和指针有何区别:

1.指针是一个对象,而引用仅是一个对象的别名

2.引用使用时无需解引用,指针需要

3.引用只能在定义时初始化一次,而指针可变

4.引用不能为空,指针可以为空

5.有多级指针没有多级引用

6.不会为引用变量开辟内存空间,它和它引用的变量共用一块内存空间,指针会开辟内存空间

7.引用自加改变引用值的内容,指针自加改变指针的指向

8.sizeof含义不同,引用结果为引用类型的大小,指针始终是地址空间所占大小

9.引用比指针使用起来相对更安全

虚函数

什么情况下使用虚函数?和纯虚函数有什么区别?虚析构函数的作用是什么?(虚函数表)

答:虚函数的主要作用是“运行时多态”。虚析构函数的作用在于使用delete删除一个对象时,能确保析构函数被正确的执行。

区别:

  1. 虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称为抽象类(abstract class)。
  2. 虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base class)只有声明而没有定义。
  3. 虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式被调用。
  4. 虚函数和纯虚函数通常存在于抽象基类(abstract base class -ABC)之中,被继承的子类重载,目的是提供一个统一的接口。
  5. 虚函数的定义形式:virtual {method body}

纯虚函数的定义形式:virtual { } = 0;

在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时候要求前期bind,然而虚函数却是动态绑定(run-time bind),而且被两者修饰的函数生命周期(life recycle)也不一样。

  1. 虚函数必须实现,如果不实现,编译器将报错,错误提示为:

    error LNK****: unresolved external symbol “public: virtual void __thiscall

    ClassName::virtualFunctionName(void)”

  2. 对于虚函数来说,父类和子类都有各自的版本。由多态方式调用的时候动态绑定。

  3. 实现了纯虚函数的子类,该纯虚函数在子类中就编程了虚函数,子类的子类即孙子类可以覆盖

    该虚函数,由多态方式调用的时候动态绑定。

  4. 虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的

    函数。

  5. 多态性指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。

a.编译时多态性:通过重载函数实现

b 运行时多态性:通过虚函数实现。

  1. 如果一个类中含有纯虚函数,那么任何试图对该类进行实例化的语句都将导致错误的产生,因为抽象基类(ABC)是不能被直接调用的。必须被子类继承重载以后,根据要求调用其子类的方法。

    1.Qt创建线程的方式有哪些
    2.信号槽实现原理
    3.Qt的消息传输机制
    4.QML和C++的交互方式
    5.如何在C++中定义model并在qml中使用
    6.moveToThread的优缺点
    7.Qt中的http是同步还是异步,如何实现同步
    8.Qt的信号槽是线程安全的吗,Qt信号的连接方式?

    Qt 支持这些信号槽连接类型:
    AutoConnection:自动连接(默认)如果信号在接收对象具有亲和性的线程中发出,则行为与直接连接相同。否则,行为与排队连接相同。
    DirectConnection:直接连接,发出信号时立即调用槽。槽在发射器的线程中执行,不一定是接收器的线程。
    Queued Connection:当控制返回到接收者线程的事件循环时调用该槽。插槽在接收者的线程中执行。
    BlockingQueuedConnection:与 Queued Connection 一样调用槽,除了当前线程阻塞,直到槽返回。
    注意:使用该类型连接同一线程中的对象会导致死锁。
    UniqueConnection:唯一连接行为 与自动连接相同,但只有在不复制现有连接时才会建立连接。即,如果相同的信号已经连接到同一对对象的相同槽,则不建立连接并且connect()返回false。

    可以通过将附加参数传递给connect()来指定连接类型。请注意,如果事件循环在接收者的线程中运行,则在发送者和接收者位于不同线程中时使用直接连接是不安全的,原因与调用位于另一个线程中的对象上的任何函数是不安全的原因相同。

    QObject::connect () 本身是线程安全的。

    9.元对象系统介绍
    10.QPixmap和QImage的区别 参考这里
    11.Qt跨平台的优缺点

C++基础篇

  • 1.线程同步的方式有哪些

  • 2.线程间通信如何实现

  • 3.进程间通信如何实现 参考这里

  • 4.IO模型用过哪些

  • 5.IO实现的方式有哪些

  • 6.用过哪些STL

  • 7.迭代器失效怎么产生的,如何避免 参考这里

  • 8.vector、list、map实现原理

  • 9.如何实现多态

  • 10.虚函数实现原理,虚函数表结构,对于菱形继承结构,虚函数表处理方式是什么

  • 11.内存结构分配方式

    1
    2
    3
    4
    5
    6
    C++ 内存分区:栈、堆、全局/静态存储区、常量存储区、代码区。
    1.栈:存放函数的局部变量、函数参数、返回地址等,由编译器自动分配和释放。
    2.堆:动态申请的内存空间,就是由 malloc 分配的内存块,由程序员控制它的分配和释放,如果程序执行结束还没有释放,操作系统会自动回收。
    3.全局区/静态存储区(.bss 段和 .data 段):存放全局变量和静态变量,程序运行结束操作系统自动释放,在 C 语言中,未初始化的放在 .bss 段中,初始化的放在 .data 段中,C++ 中不再区分了。
    4.常量存储区(.data 段):存放的是常量,不允许修改,程序运行结束自动释放。
    5.代码区(.text 段):存放代码,不允许修改,但可以执行。编译后的二进制文件存放在这里。

12.malloc free 和new delete的区别,为什么不能malloc和delete混用,会产生什么问题

13.红黑树实现原理

14.查找算法有哪些

15.排序算法有哪些

16.快排实现原理,时间复杂度是多少

17.指针和引用的区别

1
2
3
4
5
6
7
8
9
指针有自己的一块空间,而引用只是一个别名
指针的大小是4,引用则是被引用对象的大小
指针可以被初始化为NULL,而引用必须被初始化且必须是一个已有对象的引用
作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对应用的修改都会改变引用所指向的对象
可以有const指针,但没有const引用
指针在使用中可以指向其他对象,但是引用只能是一个对象的引用,不能被改变
指针可以有多级指针(**p),而引用只有一级
指针和引用使用++运算符的意义不一样
如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄漏。

18.const声明指针的问题

19.哪些运算符不能重载

1
2
3
4
5
1 . (点运算符)通常用于去对象的成员,但是->(箭头运算符),是可以重载的
2 ::(域运算符)即类名+域运算符,取成员,不可以重载
3 .* (点星运算符,)不可以重载,成员指针运算符".*,即成员是指针类型
4 ?: (条件运算符)不可以重载
5 sizeof 不可以重载

20.什么是线程安全

1
2
3
4
5
线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他操作,调用这个对象的行为都可以获得正确的结果,那么这个对象就是线程安全的。

线程安全问题大多是由全局变量及静态变量引起的,局部变量逃逸也可能导致线程安全问题。
  • 21.C++11新特性用过哪些

22.vector的C++新特性有哪些

23.讲讲右值引用与完美转发

24.对象池的使用及场景

25.线程池介绍,使用场景和实现原理

26.内存池使用场景和实现原理

27.C++的锁

28.Lambda表达式传递值有哪些方式,如何确定lambda的返回值 参考这里

29.智能指针介绍

30.shared_ptr相互引用的场景,如何解决

31.auto和decltype的区别

32.vector内存拷贝问题,什么情况下会出现内存拷贝,如何解决这个问题使其更加高效

33.vector中resize和reverse的区别

1
2
3
reserve是容器预留空间,但并不真正创建元素对象,在创建对象之前,不能引用容器内的元素,因此当加入新的元素时,需要用push_back()/insert()函数。
resize是改变容器的大小,并且创建对象,因此,调用这个函数之后,就可以引用容器内的对象了,因此当加入新的元素时,用operator[]操作符,或者用迭代器来引用元素对象。
再者,两个函数的形式是有区别的,reserve函数之后一个参数,即需要预留的容器的空间;resize函数可以有两个参数,第一个参数是容器新的大小,第二个参数是要加入容器中的新元素,如果这个参数被省略,那么就调用元素对象的默认构造函数。
  • 34.内存泄露的情况

  • 35.链表实现原理

  • 36.动态库和静态库的区别

  • 37.多线程在什么场景下使用

  • 38.动态库导出的接口设计有哪些需要注意的。工作中有没有做过动态接口设计,如何考虑不同版本的兼容性。假如已经导出了接口给外部使用,并且该版本已经发布出去了,如果新增加了接口,那么如何保障已经发出去的版本能够调用到这个新的接口,如何设计这个兼容性问题。 参考这里

  • 39.用过哪些设计模式,举例说明其原理 参考这里

  • 40.堆和栈的区别

    1
    2
    3
    4
    5
    6
    1.申请方式:栈是系统自动分配,堆是程序员主动申请。
    2.申请后系统响应:分配栈空间,如果剩余空间大于申请空间则分配成功,否则分配失败栈溢出;申请堆空间,堆在内存中呈现的方式类似于链表(记录空闲地址空间的链表),在链表上寻找第一个大于申请空间的节点分配给程序,将该节点从链表中删除,大多数系统中该块空间的首地址存放的是本次分配空间的大小,便于释放,将该块空间上的剩余空间再次连接在空闲链表上。
    3.栈在内存中是连续的一块空间(向低地址扩展)最大容量是系统预定好的,堆在内存中的空间(向高地址扩展)是不连续的。
    4.申请效率:栈是有系统自动分配,申请效率高,但程序员无法控制;堆是由程序员主动申请,效率低,使用起来方便但是容易产生碎片。
    5.存放的内容:栈中存放的是局部变量,函数的参数;堆中存放的内容由程序员控制。

  • 41.什么是二叉搜索树

    二叉搜索树,左子树的所有节点都小于当前节点,右子树的所有节点都大于当前节点,并且每棵子树都具有上述特点二叉搜索树,左子树的所有节点都小于当前节点,右子树的所有节点都大于当前节点,并且每棵子树都具有上述特点

-------------本文结束感谢您的阅读-------------