那些常见的C++、Qt基础面试题

40 篇文章 13 订阅
30 篇文章 65 订阅

前言

又到了金三银四的季节,每年这个时候都是跳槽的高峰期,在整理电脑资料的过程中发现一些之前记录的面试过程中最常提到的C++和Qt相关问题,其实都是些很基础的知识点,但是在面试过程中出镜率非常高。总结如下,仅供参考。

正文

废话不多说,直接上题。

C++基础篇
  • 1.线程同步的方式有哪些

1.互斥锁 (std::mutex):通过锁来保证同一时刻只有一个线程访问共享资源,其他线程必须等待锁的释放才能访问。
2.信号量 (std::semaphore):可以控制并发访问共享资源线程的数目。
3.条件变量 (std::condition_variable):用于多线程编程中发生线程等待的情况,可以按照一定的顺序唤醒线程。
4.原子操作 (std::atomic):基于硬件提供的CAS (compare-and-swap) 操作,可以让变量的赋值操作等价于一个原子操作,支持多线程并发。
5.读写锁 (std::shared_mutex):用于多线程读写共享资源时,读取共享资源的线程是安全的,但写入共享资源的线程需要独占资源,其余线程需要等待写操作完成。
6.屏障 (std::barrier):用于多线程编程中让多个线程进入某个点时再继续执行,可以帮助优化性能,提高并行度。
7.原子操作库 (std::atomic_library):在std::atomic的基础上提供了更多的原子操作,比如_fetch_add,_fetch_sub等等。

  • 2.线程间通信如何实现
  • 共享内存:通过在进程间共享内存来实现线程间的通信。需要设计好共享内存区域的数据结构,保证线程操作时不会发生竞态条件。
  • 消息队列:通过发送和接收不同类型的消息,在线程之间传递信息。可以使用系统提供的消息队列如POSIX消息队列、ZeroMQ,或自己在共享内存中实现一个消息队列。
  • 管道:管道是Linux系统中的一种通信方式,可以用于进程间的通信,也可以用于线程间通信。
  • 信号量:可以用于不同线程之间的同步和互斥。可以使用系统提供的信号量如POSIX信号量,也可以使用C++11库中提供的信号量。
  • 条件变量:往往用于某个线程等待另一个线程完成某些操作,然后通知正在等待的线程。
  • 互斥锁:主要用于解决多个线程访问共享数据时可能导致的竞态条件。线程通过获取锁来申明对共享资源的独占,然后再进行访问。
    在使用这些线程间通信机制时,需要根据实际场景选择最适合的机制以获得最佳的性能。
  • 3.进程间通信如何实现 参考这里
  • 4.IO模型用过哪些
  • 5.IO实现的方式有哪些
  • 阻塞IO模型(Blocking IO Model)
  • 非阻塞IO模型(Non-Blocking IO Model)
  • 多路复用IO模型(Multiplexing IO Model)
  • 信号驱动IO模型(Signal Driven IO Model)
  • 异步IO模型(Asynchronous IO Model)

其中,阻塞IO模型是默认的模型,它的效率不高,但代码实现简单;非阻塞IO模型通过不断地轮询检查IO事件的状态,实现了一定程度的并发,但也存在效率较低的问题;多路复用IO模型(如epoll或select)使用单个进程处理多个IO连接,大大降低了系统开销,提高了并发性能;信号驱动IO模型类似于多路复用IO模型,但它使用信号来通知程序有事件发生;异步IO模型(如aio或libuv)则是将IO请求提交给操作系统,等操作系统完成后再通知程序,效率最高,但使用起来复杂一些。

  • 6.用过哪些STL
  • 7.迭代器失效怎么产生的,如何避免 参考这里
  • 8.vector、list、map实现原理
  • 9.如何实现多态
  • 10.虚函数实现原理,虚函数表结构,对于菱形继承结构,虚函数表处理方式是什么

虚函数是C++中的一种实现多态性的机制,它能够在运行时动态地确定调用哪个函数,而不是在编译时就确定。虚函数通过在成员函数前加上virtual关键字来声明。

在C++中,每个具有至少一个虚函数的类都有一个虚函数表(也叫虚表)来维护函数指针。当类被实例化时,每个对象会包含一个指向该类的虚函数表的指针。当调用虚函数时,根据对象指针上的虚函数表指针查找对应的函数指针,并通过该函数指针调用虚函数的实现。

虚函数表是一个数组,其中每个元素是一个函数指针,对应一个虚函数的实现。虚函数表指针指向虚函数表的首地址。虚函数的索引就是它在虚函数表中的索引。函数的实现是通过函数指针调用的。

对于菱形继承结构,一个子类继承了多个父类,每个父类中都可能有虚函数,会导致继承链上存在多个虚函数表。为了避免出现重复的虚函数表,C++采用了虚函数表指针的共享方式。即,通过虚函数表指针共享父类的虚函数表,从而避免了数据的重复存储。C++编译器对每个子类都会在虚函数表中分配一些空间,用于存储子类自己新增的虚函数,将原来父类的虚函数表中的函数指针拷贝到子类的虚函数表中。当调用父类的虚函数时,会先通过虚函数表指针查找到父类虚函数表,然后根据虚函数在虚函数表中的索引找到对应的函数指针,最后调用函数实现。

可参考

  • 11.内存结构分配方式

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

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

malloc和free是C语言标准库中用于动态分配和释放内存的函数,new和delete是C++语言中用于动态分配和释放内存的运算符,它们的作用相同,但是使用方式和语法不同。
主要有以下几个区别:

  • malloc和free是函数,需要#include <stdlib.h>库文件;new和delete是运算符,不需要任何库文件。
  • malloc只负责给定一个大小,而并不负责构造对象;new在分配完内存后还会调用构造函数。
  • free只负责释放内存,而不会调用对象的析构函数;delete在释放内存前,还会先调用对象的析构函数。

不能混用的原因是,malloc和free的内部机制与new和delete不同,它们使用的底层内存分配算法不同。在使用malloc分配内存时,返回的是一块原始的内存空间,这块空间中并没有对象,需要手动构造对象。而使用new运算符时,它会调用类的构造函数构造对象,将对象的构造和内存的分配联系在了一起。如果混用的话,就会导致对象的构造和析构不匹配,导致内存泄漏或者程序崩溃等问题。另外,new分配的内存必须使用delete释放,malloc分配的内存必须使用free释放,否则也会出现内存泄漏等问题。

参考这里

  • 13.红黑树实现原理

  • 14.查找算法有哪些
    常见的查找算法有以下几种:

    • 线性查找(顺序查找) 时间复杂度:O(n) 空间复杂度:O(1)

    • 二分查找(折半查找) 时间复杂度:O(logn) 空间复杂度:O(1)

    • 插值查找 时间复杂度:O(log(logn)) 空间复杂度:O(1)

    • 斐波那契查找 时间复杂度:O(logn) 空间复杂度:O(1)

    • 哈希查找 时间复杂度:O(1) (平均情况) 空间复杂度:O(n)

  • 15.排序算法有哪些
    常见的排序算法有以下几种:

    • 冒泡排序 时间复杂度:O(n^2) 空间复杂度:O(1)

    • 插入排序 时间复杂度:O(n^2) 空间复杂度:O(1)

    • 选择排序 时间复杂度:O(n^2) 空间复杂度:O(1)

    • 快速排序 时间复杂度:O(nlogn) 空间复杂度:O(logn) (递归栈的空间)

    • 归并排序 时间复杂度:O(nlogn) 空间复杂度:O(n)

    • 堆排序 时间复杂度:O(nlogn) 空间复杂度:O(1)

以上算法的时间复杂度和空间复杂度都是根据具体实现方式以及数据规模而不同。需要根据具体使用场景进行选择和优化。其中,快速排序和归并排序是常用的高效排序算法,堆排序适合在数据集合较大时使用,插入排序、选择排序和冒泡排序适合在数据量较小或是数据已经基本有序时使用。

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

快速排序(QuickSort)是一种基于比较的排序算法,采用分治策略来对一个数组进行排序。其基本思想是:首先选取一个枢纽元素(pivot element),将数组划分为两个子序列,其中一个子序列中的所有元素都小于枢纽元素,另一个子序列中的所有元素都大于枢纽元素;然后对这两个子序列分别递归地进行快速排序,直到整个序列有序为止。

快速排序的基本步骤如下:

  • 首先选取一个枢纽元素(pivot element),一般可以随机选择待排序数组中的一个元素。

  • 将待排序数组划分为两个子序列,使得一个子序列中的所有元素都小于枢纽元素,另一个子序列中的所有元素都大于等于枢纽元素。这个过程称为划分(Partition)操作。

  • 对左右两个子序列分别递归地执行以上两步,直到子序列长度为1时排序结束。

快速排序中的划分操作,一般有以下几种实现方式:

  • 挖坑填数法:先将枢纽元素存入一个变量中,然后将待排序数组中第一个元素作为枢纽元素,从后往前找到第一个小于枢纽元素的元素,将其移动到前面的空位上,然后从前往后找到第一个大于枢纽元素的元素,将其移动到后面的空位上,重复此过程,直到两个指针相遇时,将枢纽元素放入最终的空位中。

  • 左右指针法:定义两个指针分别指向待排序区间的左右两端,然后从右往左找到第一个小于枢纽元素的元素,从左往右找到第一个大于等于枢纽元素的元素,交换它们的位置,重复此过程,直到左右指针相遇,然后将枢纽元素放入最终的位置。

快速排序的时间复杂度为 O(nlogn)(最坏的情况是 O(n^2)),空间复杂度为 O(logn)(最坏的情况是 O(n))。由于快速排序采用原地排序方式,不需要额外的内存空间,同时由于采用了分治策略,使得快速排序的排序速度很快,是一种常用的排序算法。

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

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

  • 20.什么是线程安全

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

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

线程安全问题大多是由全局变量及静态变量引起的,局部变量逃逸也可能导致线程安全问题。

  • 21.C++11新特性用过哪些
  • 22.vector的C++新特性有哪些
  • 23.讲讲右值引用与完美转发 参考这里1 参考这里2
  • 24.对象池的使用及场景
  • 25.线程池介绍,使用场景和实现原理
  • 26.内存池使用场景和实现原理
  • 27.C++的锁
  • 28.Lambda表达式传递值有哪些方式,如何确定lambda的返回值 参考这里
  • 29.智能指针介绍
  • 30.shared_ptr相互引用的场景,如何解决
  • 31.auto和decltype的区别
  • 32.vector内存拷贝问题,什么情况下会出现内存拷贝,如何解决这个问题使其更加高效
  • 33.vector中resize和reverse的区别

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

  • 34.内存泄露的情况
  • 35.链表实现原理
  • 36.动态库和静态库的区别
  • 37.多线程在什么场景下使用
  • 38.动态库导出的接口设计有哪些需要注意的。工作中有没有做过动态接口设计,如何考虑不同版本的兼容性。假如已经导出了接口给外部使用,并且该版本已经发布出去了,如果新增加了接口,那么如何保障已经发出去的版本能够调用到这个新的接口,如何设计这个兼容性问题。 参考这里
  • 39.用过哪些设计模式,举例说明其原理 参考这里
  • 40.堆和栈的区别

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

  • 41.什么是二叉搜索树

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

  • 42.C++中构造函数为什么不能是虚函数?
  1. 创建对象时,需要确定对象的类型,而虚函数是在运行时动态确定其类型的。在构造一个对象时,由于对象还未创建成功,编译器无法知道对象的实际类型;
  2. 虚函数的调用需要通过vptr虚函数表指针,而该指针是存放在对象的内存空间中的,若构造函数声明为虚函数,那么由于对象尚未创建,还没有内存空间,也就没有对应虚函数表来调用虚构造函数了;
  3. 虚函数的作用在于通过父类的指针或者引用,在调用它的时候能够通过动态链编调用子类重写的虚成员函数。而构造函数是在创建对象时是系统自动调用的,不可能通过父类或者引用去调用,因此就规定构造函数不能是虚函数。
Qt相关
  • 1.Qt创建线程的方式有哪些 参考这里
  • 2.信号槽实现原理

信号与槽的具体流程。

    1. moc查找头文件中的signals,slots,标记出信号和槽。
    1. 将信号槽信息存储到类静态变量staticMetaObject中,并且按声明顺序进行存放,建立索引。
    1. 当发现有connect连接时,将信号槽的索引信息放到一个map中,彼此配对。
    1. 当调用emit时,调用信号函数,并且传递发送信号的对象指针,元对象指针,信号索引,参数列表到active函数
    1. 通过active函数找到在map中找到所有与信号对应的槽索引
    1. 根据槽索引找到槽函数,执行槽函数。

参考这里

  • 3.Qt的消息传输机制

Qt的消息循环是指Qt框架内部的一个循环,主要用于处理各种事件(例如用户输入、网络数据接收等)和信号槽等异步操作。
在Qt中,消息循环主要由QCoreApplication和QEventLoop两个类来实现。QCoreApplication是Qt框架的核心类之一,它初始化了整个应用程序并提供了主事件循环。QEventLoop则负责在事件队列中循环直到队列中没有事件,处理事件时它会阻塞当前线程,直到有事件到来或者定时器到期等情况。

消息循环的原理如下:

1.Qt程序启动后,QCoreApplication会创建一个全局QEventLoop对象。
2.当用户有输入事件(如鼠标、键盘等)或者其他事件(如定时器超时、网络数据到达等)时,这些事件会被转换成Qt框架内部的事件对象,并通过QApplication::postEvent()或QCoreApplication::sendEvent()等函数添加到事件队列中。
3.QCoreApplication::exec()函数会启动事件循环,并保持应用程序处于运行状态。在循环的过程中,QEventLoop会等待事件的到来,当有事件时进行处理。如果事件队列为空,QEventLoop会一直等待,并阻塞当前线程。
4.当事件处理完成后,消息循环会继续等待下一个事件,直到exit()函数被调用或者手动中断循环。
总体来说,Qt的消息循环机制非常灵活,可以简化异步操作的实现,并且方便对事件进行管理和处理。

更多参考

  • 4.QML和C++的交互方式

QML 和 C++ 的交互方式主要分为以下两种:

  • 属性绑定:QML 提供了属性绑定的机制,可以将 QML 中的属性与 C++ 中的属性相互绑定,当 C++ 中的属性发生变化时,QML 中的属性也会相应发生变化。具体的实现方式是使用 C++ 暴露给 QML 的对象作为属性源,然后在 QML 中使用属性绑定将其绑定到对应的属性上。
  • 接口调用:QML 也可以通过 C++ 提供的接口调用 C++ 中的函数。通过 C++ 暴露给 QML 的对象的方法,在 QML 中可以使用 QML 的语法直接调用对应的函数。
  • 5.如何在C++中定义model并在qml中使用
  • 6.moveToThread的优缺点
  • 7.Qt中的http是同步还是异步,如何实现同步

异步,可以用QEventLoop实现同步

  • 8.Qt的信号槽是线程安全的吗,Qt信号的连接方式?

Qt 支持这些信号槽连接类型:
1、Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
2、Qt::DirectConnection:槽函数会在信号发送的时候直接被调用,槽函数和信号发送者在同一线程。效果看上去就像是直接在信号发送位置调用了槽函数,效果上看起来像函数调用,同步执行。
emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕
3、Qt::QueuedConnection:信号发出后,信号会暂时被放到一个消息队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,然后执行和信号关联的槽函数,这种方式既可以在同一线程内传递消息也可以跨线程操作。
emit语句后面的代码将在与信号关联的所有槽函数执行完毕后才被执行。
4、Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。而且接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
5、Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是为了避免重复连接。

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

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

  • 9.元对象系统介绍

元对象系统提供了三个功能:信号槽,运行时类型识别(RTTI),动态属性系统。但是这三个概念不是严格意义上的并列关系,我更倾向于这么记忆:元对象系统实现了动态属性系统,然后在动态属性系统的基础上,实现了信号槽、RTTI、QVariant动态类型、插件系统、跨语言调用(QML调用C++)
具体参考

  • 10.QPixmap和QImage的区别 参考这里
  • 11.Qt跨平台的优缺点

Qt跨平台的优点:

1.能够快速开发跨平台的应用程序,节省了开发时间和开发成本。

2.具有丰富的UI组件库,支持多种操作系统,包括Windows、macOS、iOS、Android、Linux等,便于开发者开发。

3.Qt提供了完整的编程框架和工具,包括Qt Creator等,可以方便地进行跨平台开发和调试。

4.Qt的API设计非常简单和易于使用,使用Qt的开发者可以轻松地为多种平台创建相同的应用程序,而无需了解每个平台的细节。

5.Qt拥有强大的消息传递机制,支持信号和槽机制和事件驱动式的编程模型,使得程序的性能更加优秀。

Qt跨平台的缺点:

Qt的跨平台性可能不如直接使用底层API的本地应用程序,可能会使得某些特定平台的应用程序不够优化。

Qt有一个相对较大的代码库,并且在开发跨平台应用程序时,可能需要包含额外的库文件和组件,增加了应用程序的大小。

Qt在不同平台之间的UI组件可能有所不同,必须进行针对性的开发,增加了开发的复杂性。

Qt可能存在性能问题,在一些特殊的应用领域可能不太适用。

综上所述,Qt作为一种跨平台解决方案,优缺点相较于其他技术都是相对的,需要根据具体的应用场景来确定使用它的可行性。但总的来说,Qt作为一个成熟和广泛使用的开发工具,它的跨平台性仍然是其最突出的优点之一。

平台相关
  • 1.win系统编程用过哪些系统接口
  • 2.windows消息传递机制

Windows消息传递机制是Windows操作系统中用于实现不同应用程序间或同一应用程序不同线程间互相通信的机制。其基本原理是将消息发送者的消息封装成一个窗口消息(Window Message)或系统消息(System Message),然后将消息发送到一个特定的窗口句柄或线程句柄,接收消息的程序或线程则通过消息循环或消息回调函数来接收和处理消息。

在Windows操作系统中,窗口句柄(Window Handle)是消息传递机制的重要概念。每一个窗口都对应一个唯一的窗口句柄,用于在系统中唯一标识一个窗口。发送消息的程序或线程通过窗口句柄来定位消息接收程序或线程。Windows操作系统中有大量的API供程序员使用,如SendMessage和PostMessage等函数都是用于发送消息到指定的窗口句柄。

另外,Windows操作系统中还有一种特殊的窗口,即控制台窗口(Console Window)。控制台窗口不是普通窗口,而是由CMD.EXE创建的,主要用于提供文本模式下的用户界面。SendMessage和PostMessage函数不能直接发送消息到控制台窗口,需要使用其他API来实现,如WriteConsoleInput和WriteConsoleOutput等函数。

  • 3.如果要自己写消息循环会怎么写
  • 4.windbg调试使用
  • 5.内存泄露如何排查和解决
  • 6.什么是消息队列

消息队列(Message Queue,简称MQ)是一种应用程序解耦和异步通信的技术。它是一种跨进程或跨网络的通信方式,用于在一个系统中不同的应用程序之间传递数据和消息。

消息队列采用一种生产者/消费者模型,在这个模型中,消息的创建者称为生产者,消息的接收者称为消费者。生产者将消息发送到队列中,而消费者则从队列中取出消息并处理它们。这种模型能够有效地解耦和应用程序,使得它们可以独立地进行开发和部署,从而提高应用程序的可伸缩性、可靠性和可维护性。
可参考

  • 7.一个进程可以有多个消息队列吗

一个进程可以创建多个消息队列。在大多数操作系统中,一个进程可以创建多个消息队列,并且每个队列都可以设置不同的属性,如队列大小和消息优先级等。一个进程可以将多个消息发送到不同的队列中,或者从不同的队列中接收消息。这种灵活性可以使得应用程序更容易进行进程间通信和协作。

  • 8.sendMessage和postMessage的区别

sendMessage和postMessage是两个不同的概念。

sendMessage通常指的是在同一进程中的线程之间传递消息,例如在Windows操作系统中使用SendMessage函数,在Android操作系统中使用Handler.sendMessage方法。sendMessage的调用是同步的,即特定的线程在发送完消息后会等待接收线程处理完消息后再继续执行。

postMessage通常指在Web Worker中向主线程或从主线程向Web Worker发送消息。postMessage的调用是异步的,即发送消息后就立刻返回,不会阻止发送消息的线程继续执行。接收线程处理完消息后会通过回调函数通知发送线程。

总的来说,sendMessage和postMessage都是用于在不同的执行上下文中传递消息,不同的是它们在同步异步处理上的区别。

  • 9.VS调试dump文件过程
网络相关
  • 1.TCP底层原理
  • 2.TCP三次握手,四次握手过程,发送的报文以及每次握手的状态,断开后为什么是time_wait状态

TCP协议在建立连接时需要进行三次握手(SYN, SYN-ACK, ACK)和四次挥手(FIN, ACK, FIN, ACK)。

TCP三次握手(Established Connection):

  • 客户端向服务器发送SYN包,表示请求连接,此时客户端进入SYN_SEND状态。
  • 服务器收到SYN包后,返回一个ACK包,同时自己也发送一个SYN包,表示同意连接,此时服务器进入SYN_RECV状态。
  • 客户端收到服务器的ACK包和SYN包后,发送一个ACK包,表示连接建立成功,此时客户端和服务器都进入ESTABLISHED状态。

TCP四次挥手(Terminate Connection):

  • 客户端向服务器发送FIN包,表示要关闭连接,此时客户端进入FIN_WAIT1状态。
  • 服务器收到FIN包后,返回一个ACK包,表示确认收到了关闭请求,此时服务器进入CLOSE_WAIT状态,客户端收到ACK包后进入FIN_WAIT2状态。
  • 服务器处理完数据后,向客户端发送一个FIN包,表示服务器也要关闭连接,此时服务器进入LAST_ACK状态。
  • 客户端收到服务器的FIN包后,向服务器发送一个ACK包,表示确认关闭连接,此时客户端进入TIME_WAIT状态,等待一定时间后关闭,服务器收到ACK包后立即进入CLOSED状态。

TCP三次握手和四次挥手保证了数据传输的可靠性和完整性。

客户端进入TIME_WAIT状态是为了确保已经发送的最后一个ACK包被服务器端正确接收,以确保这条TCP连接的数据传输的可靠性和完整性。具体原因如下:

  • 可靠关闭: 客户端向服务器发送FIN包后,服务器端收到FIN包后会发送ACK包,确保客户端的FIN包被正确接收。此时客户端进入FIN_WAIT2状态,等待服务器端发送FIN包。如果服务器端没有正确接收这个ACK包,就可能会认为客户端并没有接收到服务器端发送的FIN包,导致不必要的超时或重发,因此客户端需要等待一段时间,确保服务器端已经接收到了ACK包,从而保证TCP连接的可靠关闭。

  • 避免重复连接:客户端在TIME_WAIT状态下会等待一段时间,确保服务器端发送的FIN包能够正确到达,从而防止该连接的后续报文段被其他连接错误地使用。

总之,TIME_WAIT状态的作用是确保TCP连接的可靠关闭,保证这条连接的数据传输的可靠性和完整性。此时客户端会等待一段时间,以确保所有相关的数据都已被正确传输和接收。

  • 3.TCP粘包如何处理
  • 4.网络传输协议如何制定,哪些字段必须定义

网络传输协议制定需要考虑多方面因素,例如传输的数据类型、安全性、效率、可扩展性、兼容性等。一般而言,网络传输协议要包含以下几个方面的设计:

  • 1.传输协议首部:传输协议首部是协议定义中最重要的部分之一,它包含了协议版本、控制位、源地址、目的地址等信息,能够有效地管理传输中的数据。
  • 2.传输数据:传输协议中要定义数据传输方式,例如TCP协议中通过三次握手建立连接,传输数据时采用流式传输方式,保证数据的可靠性;而UDP协议则通过数据报方式传输数据,只提供基础的错误检查。
  • 3.传输流程控制:传输协议中还需对数据的流程进行控制,如TCP协议通过滑动窗口方法来控制数据的发送和接收,保证数据流量的平滑。
  • 4.错误处理及恢复:在数据传输过程中,可能会出现各种错误,为了保证数据的正确传输,协议中必须定义相应的错误处理机制,例如TCP协议中提供有序可靠的重传机制来避免数据丢失。

除此之外,传输协议的设计还应该考虑安全性、效率、拓展性、兼容性等问题,设计出高效、稳定、安全的传输协议。

编程相关
  • 1.手写快排代码 参考这里
  • 2.手写冒泡排序
  • 3.创建单向链表,顺序添加10个元素
#include<iostream>

using namespace std;

struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(nullptr) {}
};

ListNode* createList(int n) {
    ListNode* dummy = new ListNode(-1);
    ListNode* curr = dummy;
    for(int i = 1; i <= n; ++i) {
        int val;
        cin >> val;
        curr->next = new ListNode(val);
        curr = curr->next;
    }
    return dummy->next;
}

int main() {
    ListNode* head = createList(10);
    while(head) {
        cout << head->val << " ";
        head = head->next;
    }   
    cout << endl;
    return 0;
}

  • 4.创建红黑树,添加元素
  • 5.单项链表,如何快速查出倒数第N个数据的值
  • 6.假设内存无限大,100万个short类型的数据,如何高效排序
  • 7.vector一次循环删除多个元素
    参考:
std::vector<int> v = {1,3,5,6,3,4,7,3};
    for(auto it = v.begin(); it != v.end();){
        cout << "*it=" << *it << endl;
        if(*it == 3){
            it = v.erase(it);
        }
        else{
            it++;
        }
    }
项目相关
  • 1.libevent接口如何封装的
  • 2.介绍自己设计的项目框架
  • 3.项目中遇到的难题,如何解决的
  • 4.如何调试程序,崩溃问题如何排查
  • 5.git和svn区别 参考这里

相关

在之前的博客中也总结过一些关于C/C++ 的基础知识点,可供参考:
C/C++基础知识点(一)
C/C++基础知识点(二)
C/C++基础知识点(三)
C/C++基础知识点(四)

最后

上面这些是之前面试中遇到过的一些常见问题,另外,还整理了一份更详细的复习资料,也是关于C++基础知识以及相关经典面试题,还有一份数据结构和算法的面试手册,非常详细,有需要的可以下载学习。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

以上资料附带详细答案。
下载地址

  • 19
    点赞
  • 343
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
对不起,我无法提供特定的面试八股文。但是,我可以告诉你一些常见面试和答案,以帮助你在C++和Qt领域的面试中更好地准备。 1. 什么是C++的多态性(Polymorphism)? 答:多态性是面向对象编程的一个重要概念。它允许不同的对象对相同的函数或方法做出不同的响应。通过使用虚函数(virtual function)和继承(inheritance),我们可以实现运行时多态。 2. 什么是Qt框架?它的优点是什么? 答:Qt是一个跨平台的应用程序开发框架,用于开发图形用户界面(GUI)和非GUI应用程序。它具有以下优点: - 跨平台:可以轻松地在不同操作系统上运行。 - 高效性:Qt具有高性能和低资源消耗。 - 扩展性:提供丰富的类库和工具,可以轻松扩展应用程序。 - 开发效率:Qt提供了易于使用的API和开发工具,可以加快开发速度。 3. 什么是信号与槽机制(Signals and Slots)? 答:信号与槽机制是Qt中一种用于对象间通信的机制。通过使用信号和槽,一个对象可以发送信号,而另一个对象可以接收并响应该信号。这种机制使得对象之间的解耦更加容易,同时也提供了一种灵活的方式来处理事件和数据传递。 4. 什么是元对象系统(Meta Object System)? 答:元对象系统是Qt中的一种特性,它允许在运行时获取类的信息。元对象系统通过使用MOC(Meta Object Compiler)将元对象信息嵌入到C++类中。这使得Qt能够实现信号与槽机制、动态属性、反射等高级功能。 请记住,面试八股文只是准备面试的一部分,更重要的是理解基本概念并能够将其应用到实际问中。在面试之前,确保你对C++和Qt基础知识有扎实的掌握,并且有实际项目经验能够展示你的技能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luoyayun361

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值