最近在找工作…面试中遇到了的问题总以为自己会但回答的时候磕磕巴巴,觉得还是要总结一下:
- vector和list的区别
vector list
底层数据结构 基于动态数组实现,元素在内存中连续存储 基于双向链表实现,元素在内存中非连续存储,每个节点包含指向前后节点的指针
随机访问性能 支持高效的随机访问,可直接通过下标或迭代器偏移访问元素 不支持随机访问,需遍历链表寻找目标元素
插入/删除性能 尾部插入/删除高效,中间或头部插入/删除需要移动后续元素,可能导致内存重分配 任意位置的插入/删除仅需调整指针,无需移动其他元素。
需要先遍历到目标位置
内存管理 内存连续,缓存友好(访问速度快)。
扩容时会重新分配内存,导致原有迭代器失效。 内存非连续,缓存不友好(可能引发缓存未命中)。
插入/删除不会导致迭代器失效(除非删除当前元素)。
空间开销 仅存储元素本身,空间效率高 每个节点需要额外存储两个指针(前驱和后继),空间开销较大(尤其是元素较小时)
迭代器类型 支持随机访问迭代器 仅支持双向迭代器
应用场景 需要频繁随机访问。
插入/删除主要在尾部(如栈结构)。
元素类型较小且数量较多(内存连续优势明显)。 需要频繁在任意位置插入/删除。
不需要随机访问,仅需顺序访问。
元素较大时(避免移动开销)。 - XML和JSON文件区别
XML JSON
结构 标签嵌套(如<tag>data</tag>
),结构严格,支持属性(如<name id="1">
) 键值对(如{"key": "value"}
),结构更简洁,不支持属性,数据直接通过值表示。
可读性 冗长,标签重复较多 更紧凑,语法接近代码对象,易读
数据体积 文件较大(标签占用额外空间) 体积更小,传输效率高
解析与使用 需复杂解析(如 DOM/SAX),适合强类型数据(如配置文件) 解析简单(直接映射为对象),广泛用于 Web API 和前后端交互。
应用场景 文档型数据(如 HTML)、企业级配置(如 Spring) 轻量级数据交换(如 RESTful API)、移动端和 JavaScript 应用 - 进程和线程的关系
进程 线程
定义 操作系统资源分配的基本单位,独立内存空间 CPU调度的基本单位,共享进程内存空间
资源分配 独立拥有代码、数据、堆栈、文件句柄等资源 共享进程资源,仅独享寄存器和栈
通信方式 管道/消息队列/共享内存等 直接读写进程内存
性能差异 进程切换开销大(涉及资源切换) 线程切换开销小(仅切换上下文)
应用场景 多进程适合需要高隔离性的任务 多线程适合需要频繁通信的任务 - SQL语言核心要点
DDL(数据定义) CREATE/ALTER/DROP
DML(数据操作) SELECT/INSERT/UPDATE/DELETE
DCL(数据控制) GRANT/REVOKE
TCL(事务控制) COMMIT/ROLLBACK - C++模板(Template)详解
- 函数模板:
template <typename T>
T max(T a, T b) {return (a > b) ? a : b;
}
2. 类模板:
```cpp
template <class T>
class Stack {
private:std::vector<T> elements;
public:void push(T const&);T pop();
};
**高级特性**:
- 模板特化(特殊类型定制实现)
- 可变参数模板(template<typename... Args>)
- 模板元编程(编译期计算)
6. **链表数据结构解析**
**基础结构**:
```cpp
struct Node {int data;Node* next;
};
类型 特点 插入复杂度单链表 单向遍历 O(1)
双向链表 双向指针,可逆序遍历 O(1)
循环链表 尾节点指向头节点 O(n)
7. **UDP vs TCP对比**
特性 TCP UDP
连接方式 面向连接(三次握手) 无连接
可靠性 保证送达和顺序 尽力而为
传输形式 字节流 数据报文
头部大小 20-60字节 8字节
流量控制 滑动窗口机制 无
拥塞控制 多种算法 无
应用场景 网页浏览(HTTP)、文件传输(FTP) 视频流媒体、DNS查询、在线游戏
8. **结构体(struct)和类(class)区别的详细解析:**
特性 Struct Class
默认访问权限 public private
默认继承权限 public private
语义定位 数据聚合 对象封装
典型用途 轻量级数据容器(如坐标点、配置参数)
无需封装的数据集合
C语言兼容性保留 封装复杂对象(如文件处理器、网络连接)
需要隐藏实现细节的场景
面向对象特性(继承、多态)两者在内存布局、性能表现上完全一致,区别仅在于编译器对访问权限的检查。
- **模板元编程中的互换性**:```cpptemplate <typename T> // T可以是struct或classvoid process(T obj) {// ...}
9. **重载的核心规则**
同名函数/方法:多个函数使用相同的名称。
参数列表必须不同:参数类型、数量或顺序至少有一项不同。
返回值类型不影响重载:仅返回值不同不足以构成重载。
10. **重载的应用场景**
简化接口:对相似功能提供统一的名称,例如 `print` 函数处理不同类型数据。
增强灵活性:通过不同参数类型或数量,适应多种调用需求。
代码可读性:用一致的命名表达相同逻辑的不同变体。
11. 在 C++ 中,**模板(Template)** 是一种支持泛型编程的机制,允许编写与数据类型无关的通用代码,从而提高代码复用性。以下是模板的核心用法和关键概念的详细描述:模板的核心作用 泛型编程:通过抽象数据类型,让同一段代码适用于多种类型
避免代码冗余:无需为不同类型重复编写逻辑相同的代码(如 int、double版本的排序函数)
类型安全:在编译期确定类型,比宏或 void* 更安全。
模板的分类与语法 定义:用 template 关键字声明,使用 typename或 class定义泛型参数。
语法:
template<typename T> // 或 template <class T>
返回值类型 函数名(参数列表) { ... } 例子:```cpp
template <typename T>T max(T a, T b) {
return (a > b) ? a : b;}// 调用cout << max(3, 5); // T 推导为 intcout << max(3.5, 2.1); // T 推导为 double类模板定义:允许类成员的数据类型或方法参数泛型化
语法:template <typename T>class 类名 {// 成员变量和方法使用 T};template <typename T>class Stack {private:vector<T> elements;public:void push(T const& elem) {elements.push_back(elem);}T pop() {T elem = elements.back();elements.pop_back();return elem;}};// 使用Stack<int> intStack; // 存储 int 的栈Stack<string> strStack; // 存储 string 的栈
模板参数的扩展 多类型参数 template <typename T1, typename T2>void printPair(T1 a, T2 b) {cout << a << ", " << b << endl;}非类型模板参数:允许传递常量值(如整型、指针或引用)作为模板参数 template <typename T, int size>class Array {private:T data[size];public:T& operator[](int index) { return data[index]; }};Array<int, 10> intArray; // 固定大小为 10 的数组默认模板参数 template <typename T = int>class Box {T content;};
Box<> defaultBox; // 使用默认类型 int
模板特化 目的:针对特定类型提供优化或特殊实现 // 通用模板template <typename T>void print(T value) {cout << "Generic: " << value << endl;}// 特化版本(处理 char*)template <>void print<char*>(char* value) {cout << "Specialized (char*): " << value << endl;}// 调用int n = 10;print(n); // 调用通用版本char str[] = "Hello";print(str); // 调用特化版本
模板的编译机制 实例化:模板代码在编译时根据具体类型生成实际代码 // 隐式实例化(由编译器自动推导)
max(3, 5); // 生成 int max(int, int)
// 显式实例化(手动指定类型)
template double max<double>(double, double);
模板的注意事项 代码膨胀:过多模板实例化可能导致编译后代码体积增大。
编译错误延迟:模板错误通常在实例化时才会暴露。
声明与定义需在同一个文件:模板代码通常直接写在头文件中(因编译机制限制)。
4. C++11 后的扩展:支持变长模板参数(Variadic Templates)。
典型应用场景 容器类:如 vector<T>、map<K, V>。
算法泛化:如 sort()可处理任何支持比较操作的类型。
数学库:如矩阵运算类 Matrix<T>。
智能指针:如 shared_ptr<T>。
通过模板,C++ 实现了高度灵活且类型安全的泛型编程,是 STL(标准模板库)的基石。合理使用模板可以大幅减少冗余代码,同时保持高性能。
12. **堆和栈的联系与区别**
特性 栈(Stack) 堆(Heap)
内存分配方式 由系统自动分配和释放(如函数调用时) 由程序员手动申请和释放(如 new/delete)
生命周期 随函数调用结束自动释放(局部变量) 需显式释放,否则可能内存泄漏
空间大小 较小 较大(受限于操作系统可用内存)
分配效率 快(只需移动栈指针) 慢(需动态查找可用内存块)
内存碎片 无 可能存在(频繁分配释放后)
生长方向向低地址方向增长 向高地址方向增长
线程共享 每个线程独享自己的栈 进程内所有线程共享堆
存储内容 局部变量、函数参数、返回地址等 动态分配的对象、数据结构(如数组、对象)
典型应用场景 存储函数调用的上下文(如函数参数、返回地址)。
存放局部变量(生命周期与函数调用绑定)。
递归调用时,每次调用会生成新的栈帧。 动态分配内存(如 `new`、`malloc`)。
存储大型对象或需要长期存在的数据(如全局数据结构)。
联系与协同:均为程序运行时提供内存空间,支持数据存储和操作 栈用于短期、快速的内存需求(如函数调用)
栈上的指针变量可以指向堆内存(实现动态内存管理):
int* arr = new int[100]; // 栈变量 arr 指向堆内存
对用于灵活、长期的内存需求(动态数据结构)
常见问题 栈溢出:递归过深或局部变量过大(如超大数组)栈空间不足导致崩溃。 内存泄露:堆内存未释放。
野指针:堆内存释放后,指针未置空。
适用场景 短期、小数据、自动管理
高效、安全、无碎片
空间有限、不灵活 长期、大数据
容量大、生命周期可控
管理复杂、可能泄露或碎片化13. **Qt信号与槽机制的优势与不足**:
优点:类型安全,信号的参数类型与个数同接受该信号的槽的参数类型和个数相同
松散耦合:激发信号的qt对象不需要关心是否被接受,只需在使用时发出信号即可
灵活:一对多,多对一都可
不足:速度较慢(与回调函数相比)
信号与槽本质:对象间通信的机制,当信号被触发时通过qt的元对象系统动态链接,自动调用与之关联的槽函数,
14. **Qt的TCP通讯流程**
创建QTcpSocket对象(作为客户端或服务器的通信端点)
连接到主机(客户端使用connectToHost函数连接到主机的IP和端口,服务器端使用QTcpServer监听指定的本地IP和端口号)
建立连接(connected)
读写数据(QIODevice中的read write函数)
处理收发数据(启动一个事件循环,例如QEventLoop处理读写操作)
关闭连接
15. **Qt的UDP通讯流程**
创建QUdpSocket对象
若接收数据,必须绑定端口
发送和接收数据
16. **多线程使用方法**
法一:创建一个类从QThread类派生,在子线程类中重写run函数,处理操作写入run函数中
在主线程类中创建子线程对象,启动子线程,调用start函数
法二:使用回调函数,派生线程类中添加回调函数,使用typedef int(*RUNHANDLE )(void *)声明回调函数句柄,在子线程类中定义开始和结束函数,然后在需要使用线程的类中定义操作数据的回调函数以及调用回调函数的函数……
17. **多线程下信号槽分别在什么线程中执行,如何控制?**
默认直接连接,信号和槽属于同一个对象,且未使用QueuedConnection等跨线程连接方式
自动连接,信号和槽分别属于不同对象且处于不同线程,可使用AutoConnection(根据对象所在线程自动选择直接执行或跨线程执行)或QuenuedConnection(将槽函数放入接收者对象所在事件队列,在所属线程中执行)跨线程连接
线程间同步可使用互斥锁(QMutex)或其他线程同步机制实现对共享资源的访问控制,确保同一时间只有一个线程可访问
使用读写锁(QReadWriteLock)允许多个线程同时读取数据,,但写入时需独占访问,适用于读多写少的情况
信号和槽机制:主线程发射信号,子线程中接收信号同步数据
条件变量:允许一个或多个线程等待某个条件为真时再继续执行,使用QWaitCondition可实现更复杂的线程同步场景,如生产者-消费者模式
原子操作:QAtomicint、QAtomicPointer等保证特定类型的操作是原子性的。
QMutexLocker:对Mutex加锁或解锁(unlock)
18. **QObject的作用**
QObject是Qt框架中的一个基类,是所有Qt对象的父类,QObject使得Qt对象可拥有信号与槽机制、事件处理、对象生命周期管理、父子关系以及内存管理,一个QObject对象可通过继承QObject基类自定义控件或重写虚函数处理鼠标、键盘等事件
19. **Lambda机制**:在Qt信号处理中,Lambda表达式常被用于将信号连接到槽函数或进行信号响应处理,简洁、方便,可直接在连接信号时即时编写信号处理逻辑,无需定义槽函数。
20. **C++段错误原因:**
空指针解引用:使用一个未初始化或被释放的指针进行访问时会导致段错误;
数组越界访问:如果访问了数组范围外的元素,超出数组边界
栈溢出:递归调用层次过深、局部变量过多可能导致栈溢出,进而引发段错误
内存泄漏和非法内存操作:动态分配内存未释放或释放后继续访问
内存泄漏排查方式
QtCreator中的内存分析器
使用智能指针(QSharedPointer、QWeakPointer)
在析构函数中手动删除动态分配的内存
检查对象的生命周期
21. **Qt三大核心机制**
信号与槽、事件系统(Qt基于事件驱动的编程模型,每个QObject派生类都能接收和处理事件,当用户执行某操作或系统状态发生改变时,Qt自动生成并分发相应事件进而响应事件)、对象模型(使用元对象系统支持运行时类型信息和反射,如动态属性、动态调用等)Qt的元对象编译系统(MOC)创建新的文件,将源码转换为C++编译器可识别的代码,当类中需要使用信号槽、对象属性等扩展功能时可使用,其他情况使用会增大源码体积,使用方法为继承QObject,添加Q_OBJECT
22. **虚函数的实现**
虚函数是在父类中定义的一种特殊类型的函数,允许子类重写该函数以适应自身需求
通过将函数声明为虚函数,可使继承层次结构中的每个子类都能使用其自己的实现,提高代码的可扩展性和灵活性。即同一个函数名可在不同子类中表现出不同的行为。
虚函数可避免静态绑定,使用父类指针或引用调用子类对象的成员函数时默认进行静态绑定;
抽象类是一种不能直接实例化的类,只能被其他类继承并实现其虚函数,通过定义纯虚函数可使一个类成为抽象类,强制其子类必须实现该函数。
23. **多态类型**:
编译时多态(静态多态):函数重载、运算符重载
运行时多态(动态多态):虚函数、接口和抽象类(纯虚函数)
24. **C++中死锁原因及解决方法**
互斥条件:一次只有一个进程可使用该资源
占有和等待条件:一个进程至少持有一个资源,并在等待获取其他进程持有的附加资源时,不释放已占有的资源
不可抢占条件:资源只能由持有它的进程主动释放
循环等待条件:链中每个进程都占有下一个进程所需资源
避免死锁:一次性获取所有锁,同时锁定
确保资源释放后再进行下一步操作,避免长时间持有锁(提前释放资源)
避免无限等待,设置超时退出操作
25. **项目中界面卡顿,怎么优化?**
防抖:合并高频刷新请求,避免冗余渲染
节流:设置固定刷新频率
主线程中仅渲染显示,子线程中生成分析数据
内存预分配,避免动态内存分配开销太大
26. **Static和const使用**
Static静态变量声明,分为局部静态变量、全局静态变量、类静态成员变量(被类的所有对象共享,必须在类外初始化,不可以在构造函数内初始化),也可修饰类成员函数(所有对象共享该函数,不含this指针,不可使用类中非静态函数)
Const常量声明:const和static不可同时修饰类成员函数,const修饰成员函数表示不能修改对象的状态,static修饰成员函数表示该函数属于类不属于对象;
27. **C++指针和引用**
相同点:二者都可用于实现变量的间接访问,通过引用或指针可修改所指向或引用的对象
引用和指针都可作为函数参数传递,以便在函数内部修改实参的值;
不同点:引用是个别名,定义时必须初始化,并且不能被重新绑定到其他对象,指针可通过赋值操作改变其指向的对象
引用必须始终指向一个有效的对象,指针可为空或可选择性地指向任意类型的对象
引用无需显式的内存管理,自动绑定到另一个对象并随该对象的生命周期自动更新,指针需要手动分配和释放内存
对引用的操作直接影响原始对象,对指针的操作需要解引用才能访问所指向的对象。
28. **常用数据结构**
Vector:向量,连续存储,可随机访问;
Deque:双向队列,连续存储,随机访问;
List:链表,内存不连续,不支持随机访问;
Stack:栈,不可随机访问,只允许在开头增加或删除元素;
Queue:单向队列,尾部增加,开头删除;
Set、Map:集合,采用红黑树实现,可随机访问
Hash_set:哈希表,随机访问
29. **封装、继承和多态的理解**
封装是将数据(属性)和操作数据的方法(函数)结合在一起形成一个对象,通过访问修饰符(public,private和protected)控制对类成员的访问。
继承就是定义一个类(子类或派生类)继承另一个类(基类或父类)的属性和方法,继承可重用代码,实现代码的复用和扩展;
多态:即使用统一的接口操作不同的对象,主要通过虚函数和抽象类实现。
30. **重载和重写的区别**
定义不同:重载是在一个类中定义多个方法,名称相同但参数列表不同(如参数个数、类型、顺序)
重写是在子类中重新定义父类已经存在的方法,方法名、参数列表和返回类型必须与父类方法相同,但方法体不同;
范围不同:重载发生在类内部,重写发生在子类和父类之间
多态性不同:重载属于静态绑定,重写属于动态绑定
参数和返回类型:重载参数列表必须不同,重写的返回类型和参数列表必须与父类方法相同;
修饰符限制:重载无要求,重写对子类方法的访问权限不能低于父类方法的访问权限。
31. **信号和信号量的区别?**
信号是一种软件中断,通过异步方式通知进程发生了特定事件;
信号量(QSemaphore)是一种整数计数器,用于实现同步与互斥,通过等待(P)和释放(V)操作控制资源访问,防止竞态条件。创建信号量对象,通过调用acquire方法获取资源,资源不足,线程阻塞直到资源可用,调用完成后通过release释放资源,多用于线程同步。
32. **QT Connect 函数的第五个参数:**
1)自动连接(AutoConnection),默认的连接方式,如果信号与槽,也就是发送者与接受者在同一线程,等同于直接连接;如果发送者与接受者处在不同线程,等同于队列连接。
2)直接连接(DirectConnection),当信号发射时,槽函数立即直接调用。
3)队列连接(QueuedConnection),当控制权回到接受者所在线程的事件循环时,槽函数被调用。槽函数在接受者所在线程执行,即槽函数与信号接受者在同一线程
4)锁定队列连接(BlockingQueuedConnection)
Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
5)单一连接(UniqueConnection):避免重复连接,与其他类型按位或结合使用。
33. **C++的智能指针**
Std::shared_ptr:共享所有权(多个指针共享对象),引用计数,计数为0时删除对象
Std::unique_ptr:独占所有权(不支持共享引用),自动删除(超出作用域或重置时删除对象),可通过转移构造函数或复制操作符转移所有权。
Std::weak_ptr:不增加引用计数,可转换为shared_ptr,转换时要检查对象是否存在。
34. **C++信号和槽的调用流程**
Moc查找头文件中的signal和slots,标记处信号槽,将信号槽信息存储到staticMetaObject中并按照声明的顺序进行存放,建立索引;
Connect连接,将信号槽的索引信息放到一个双向链表中,彼此配对,emit被调用,调用信号函数,传递发送信号的对象指针、元对象指针,信号索引、参数列表到active函数;
Active函数在双向链表中找到所有与信号对应的槽索引,根据索引找到槽函数。