当前位置: 首页 > news >正文

C++与QT高频面试问题(不定时更新)

C++面试题


一 智能指针

1. 要解决的问题

传统的c++中,动态内存管理需要使用到(new与delete)来对指针创建与销毁,这就涉及到两个问题

  • 内存泄露问题(忘记了delete)
  • 悬空指针/非法访问(即提前释放了正在使用的内存,或重复释放同一块内存)

2. 绑定对象的生命周期

  • 构造时获取资源(初始化时指向动态内存)——即创建

  • 析构时自动释放资源(对象离开作用域时自动调用delete)——即销毁
    这种机制保证了异常安全——即使程序抛出异常,栈展开过程也会调用已构造对象的析构函数,从而确保资源被释放。

作者说:底层原理就是,我们不应该在堆内存中创建与销毁指针,而是应该在栈内存中创建与销毁指针,不管是否使用堆内存的指针,都要放到类的构造函数中进行资源的分配,且将对象本身放在栈上,利用c++的栈对象自动析构的机制释放,这样就适应了类的整个生命周期,从而避免了以上的两个问题。

3. 智能指针的创分类

C++11在<memory>头文件中提供了三种主要的智能指针,它们通过管理所指对象的所有权来工作。

1. std::unique_ptr (独占所有权指针)

即同一时刻只能有一个指针指向同一个对象,不可拷贝与赋值(避免了悬空指针问题)

1. 两种创建(c++14之后,使用make——unique分配内存)

std::unique_ptr<T> p1= std::make_unique<T>(77);

std::unique_ptr<T> p2(new int (22));

2. 使用(通过*来解引用,与指针使用方法一致)

cout << *p1<< endl;

3.三个函数(release与delete配合使用)

p1.release();                   

  • 指针仍然存在,置空所管理的对象

delete p1.get();

  • 代表释放p1

p2.reset();                                 

  • p2.reset(nullptr);                   //代表释放当前资源并置空
  • p2.reset(new int(20));       //转移所有权到新资源

4. 经典陷阱题

p1.reset(p2.get); ——move()解决。

5. 性能高,在经过编译器优化之后,性能仅次于new与delete。

2. std::shared_ptr (共享所有权指针)()

同一时刻可以有多个指针指向同一个对象(共享对象与资源)

1. 引入计数机制(计数也是指针)

确保该对象在最后一个对象销毁时,自动释放资源(避免资源泄露与悬挂指针)

1. 创建(c++14之后,使用make分配内存)

 p1= std::make_shared<T>(77);

2. 多个线程来copy shared_ptr 是被允许的,但是一块来修改他管理的对象(不安全,需使用mutex互斥量,进行保护)

3. std::weak_ptr (弱引用指针)

主要用于观察用shared_ptr管理的对象(不参与对象的生命周期管理与引用计数

1. 因为是观察shared_ptr管理的对象,所以,即使存在有多个weak_ptr指向对象,当shared_ptr被销毁,该对象仍然被释放

2. 由于是弱引用,没有对象的管理权,所以不能被解引用,可以使用lock函数,临时提升为shared_ptr,

4. 总结与对比

特性std::unique_ptrstd::shared_ptrstd::weak_ptr
所有权独占共享无(弱引用)
拷贝不允许(仅移动)允许(增加引用计数)允许(不增加引用计数)
性能开销极小(几乎无额外开销)较大(维护引用计数,原子操作)shared_ptr类似
主要用途独占资源的默认选择需要共享所有权的复杂场景打破shared_ptr循环引用
推荐创建方式std::make_uniquestd::make_sharedshared_ptr构造

5. 核心原则

  1. 首选std::unique_ptr:除非明确需要共享所有权,否则应使用它。它是管理动态资源最安全、最轻量的方式。

  2. 其次考虑std::shared_ptr:当确实需要多个实体共享对象生命周期时使用。

  3. 使用std::weak_ptr作为shared_ptr的辅助:用于打破循环引用或提供非拥有性访问。

  4. 优先使用make_*函数std::make_uniquestd::make_shared在异常安全性和性能上通常优于直接使用new

  5. 避免混合使用智能指针和原始指针:不要用同一个原始指针初始化多个智能指针,也不要手动删除get()返回的原始指针。

二 多线程

1. 要解决的问题

1. 解决主线程堵塞的问题(大文件加载导致的页面卡顿无响应的等等)

2. 利用性能,提升效率

1. 实现多线程的方式

1. Thread类

2. 

三 进程线程间的通信(重点锁)

其实这个需要重点关注锁机制,即线程与通信的,如何保证线程安全。

1. 互斥锁(Mutex)(阻塞锁)

  • 互斥锁是最基本的锁类型,用于确保同一时间只有一个线程可以访问共享资源。

适用场景

  • 保护简单的临界区

  • 需要确保只有一个线程访问共享资源的情况

缺点

  • 睡眠和唤醒操作涉及上下文切换,有一定开销

  • 使用不当可能导致死锁

  • 只能提供基本的互斥功能,无法满足复杂同步需求

创建方式

  • 引入头文件<mutex>
  •  对某个对象进行加解锁,然后引导线程去访问

实际操作的问题

  • 如果你使用的是window 32位的 mingw64 会创建线程与互斥锁发生未定义的情况,需要使用posix版本的或者引入<windows.h>的头文件。
#include <iostream>
#include <thread>
#include <mutex>  std::mutex mtx;
int shared_data = 0;void increment() {mtx.lock();shared_data++;mtx.unlock();
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Shared data: " << shared_data << std::endl;return 0;
}

2. 读写锁(Read-Write Lock)(阻塞锁)

  • 读写锁允许多个读线程同时访问共享资源,但写线程独占资源。读锁是共享的,写锁是排他的。
  • 写锁有最高优先性

实现原理

  • 当没有写锁时,多个读锁可以同时获取

  • 当有写锁时,所有读锁和写锁都会被阻塞

  • 写锁请求会优先于读锁(避免写线程饥饿)

优点

  • 读操作并发性好,适合读多写少的场景

  • 提高了系统吞吐量

缺点

  • 实现相对复杂

  • 如果写操作频繁,读操作可能会被阻塞

  • 可能导致写线程饥饿

3. 自旋锁(Spinlock) (非阻塞锁)

即访问不成功,忙等待

4. 条件变量 (Condition Variable)(配合互斥锁使用)

  • 条件变量用于线程间的等待和通知机制,通常与互斥锁配合使用。它允许线程在某个条件不满足时等待,当条件满足时被唤醒。

优点

  • 避免了忙等待,节省CPU资源

  • 支持复杂的同步模式

5. 递归锁 (Recursive Mutex)

  • 递归锁允许同一个线程多次获取同一个锁,而不会导致死锁。每次获取锁后必须释放相同次数。

6. 屏障 (Barrier)

  • 屏障用于同步多个线程,要求所有线程都到达屏障点后才能继续执行。(即线程到达阈值,则可以进行访问读写)

7. 总结比较

锁机制优点缺点适用场景
互斥锁简单,节省CPU上下文切换开销通用临界区保护
读写锁读并发性好写操作可能饥饿读多写少
自旋锁无上下文切换忙等待消耗CPU短临界区,多核
递归锁可重入可能隐藏设计问题递归或可重入函数
条件变量避免忙等待使用复杂,虚假唤醒条件等待
信号量控制访问数量可能死锁资源池,生产者消费者
屏障线程同步简单必须等待所有线程并行计算阶段同步
无锁编程高性能,无死锁实现复杂,活锁风险高性能并发数据结构

四 STL库(Standard Template Library 标准模板库)

  • 提供了一系列通用的模板类和函数,实现了常用的数据结构和算法。STL 的核心思想是泛型编程,通过模板技术实现代码的通用性和复用性。

1. 容器(Containers) - 管理数据的集合

顺序容器
1. vector(动态数组)

2. deque(双端队列)
3. list(双向链表)
4. forward_list(单向链表)
5. array(静态数组)
关联容器
1. set(集合)
2. multiset(多重集合)
3. map(映射)
4. multimap(多重映射)
无序关联容器
1. unordered_set
2. unordered_multiset
3. unordered_map
4. unordered_multimap
 容器适配器
1. stack(栈)
2. queue(队列)
3. priority_queue(优先队列)

2. 算法(Algorithms) - 处理数据的通用函数

3. 迭代器(Iterators) - 访问容器元素的通用接口

4. 函数对象(Functors) - 类似函数的对象

 5. 总结


文章转载自:

http://qxX4qd60.grtwn.cn
http://rCOWDQ1D.grtwn.cn
http://beDEPfNp.grtwn.cn
http://WnqVJ7Va.grtwn.cn
http://vAlVkMYM.grtwn.cn
http://Sd00LWhd.grtwn.cn
http://nsKOwdRK.grtwn.cn
http://fpPo2goZ.grtwn.cn
http://BLa9ukJz.grtwn.cn
http://4IZPa30H.grtwn.cn
http://MXi12sQ5.grtwn.cn
http://rVD4DCtH.grtwn.cn
http://jyebYj8M.grtwn.cn
http://KR65QMsK.grtwn.cn
http://2lmHdIGI.grtwn.cn
http://H0ygimkW.grtwn.cn
http://WtHzCDlB.grtwn.cn
http://uzb6blbP.grtwn.cn
http://bbMjm0oD.grtwn.cn
http://c1MN8dJv.grtwn.cn
http://rm8r7SS3.grtwn.cn
http://DFrd7288.grtwn.cn
http://GVDbLe9J.grtwn.cn
http://p9s7tMDz.grtwn.cn
http://W5i3muoj.grtwn.cn
http://3pECKDC4.grtwn.cn
http://atOz5n95.grtwn.cn
http://xkmWezHS.grtwn.cn
http://Z4NGJX2Q.grtwn.cn
http://OhXezwgl.grtwn.cn
http://www.dtcms.com/a/378160.html

相关文章:

  • 数据结构之跳表
  • 记录豆包的系统提示词
  • Docker 从入门到实践:容器化技术核心指南
  • 【Python-Day 43】告别依赖混乱:Python虚拟环境venv入门与实战
  • CF702E Analysis of Pathes in Functional Graph 题解
  • 元宇宙与智慧城市:数字孪生赋能的城市治理新范式
  • es通过分片迁移迁移解决磁盘不均匀问题
  • 深入浅出CRC校验:从数学原理到单周期硬件实现 (2)CRC数学多项式基础
  • 无人设备遥控器之控制指令发送技术篇
  • LinuxC++项目开发日志——高并发内存池(4-central cache框架开发)
  • 解决蓝牙耳机连win11电脑画质依托答辩问题
  • 农业养殖为何离不开温湿度传感器?
  • Android开发 AlarmManager set() 方法与WiFi忘记连接问题分析
  • CKA02-Ingress
  • JavaEE 初阶第二十一期:网络原理,底层框架的“通关密码”(一)
  • TOL-API 基于Token验证文件传输API安全工具
  • 构建一个优雅的待办事项应用:现代JavaScript实践
  • 计算机视觉进阶教学之图像投影(透视)变换
  • 计算机视觉与深度学习 | 基于MATLAB的AI图片识别系统研究
  • 计算机视觉----图像投影(透视)变换(小案例)
  • Docker 学习笔记(七):Docker Swarm 服务管理与 Containerd 实践
  • 3-10〔OSCP ◈ 研记〕❘ WEB应用攻击▸XSS攻击理论基础
  • 微信小程序开发笔记(01_小程序基础与配置文件)
  • ArcGIS JSAPI 高级教程 - ArcGIS Maps SDK for JavaScript - 自定义(GLSL)修改高亮图层样式
  • idea npm install 很慢(nodejs)
  • Elasticsearch 创建索引别名的正确姿势
  • Kite Compositor for Mac v2.1.2 安装教程|DMG文件安装步骤(Mac用户必看)
  • 深入探索 Unity 错误排查过程:从“滚动条问题”到“鼠标悬浮异常”
  • 【设计模式】从游戏角度开始了解设计模式 --- 抽象工厂模式
  • 南京大学实现非线性光学新范式丨《Light》报道光电可调谐液晶二次谐波衍射研究