经维恒润 软件开发-C++ 一面
1. 介绍一下 DDS
答案:DDS,全称 Data Distribution Service,是一种面向数据、以发布订阅为核心的分布式通信中间件标准。它和传统“请求-响应”式通信不太一样,DDS 更强调数据空间、主题、QoS 和实时分发,发布者和订阅者之间通常不需要直接感知彼此地址,而是围绕 Topic 进行通信。它的优势主要体现在低时延、高吞吐、强 QoS 控制以及天然支持去中心化发现机制,在工业控制、车载、机器人、仿真系统这些对实时性和可靠性要求高的场景里比较常见。如果往深一点说,DDS 不只是“发消息”,它更像一套完整的数据分发模型,核心对象一般包括 DomainParticipant、Publisher、Subscriber、DataWriter、DataReader,真正落地时还会大量涉及历史深度、可靠性、持久性、Deadline、Liveliness 等 QoS 配置。
2. DDS 和 Kafka、MQ 这类消息中间件有什么区别
答案:DDS 和传统消息队列最大的区别在于通信模型和目标场景。像 Kafka、RocketMQ 这类中间件更偏业务消息流转、削峰填谷、异步解耦,通常接受毫秒级甚至更高延迟,也依赖中心化 Broker。DDS 更偏实时数据分发,强调端到端时延、数据一致性感知和 QoS 可控,很多实现支持去中心化发现,不一定依赖中心 Broker。另外,MQ 通常更关注“消息是否到达、是否持久化、是否可回溯”,DDS 更关注“当前数据状态如何同步给需要它的节点”。所以如果是日志、订单、异步任务流转,一般不会优先选 DDS;如果是自动驾驶、雷达融合、机器人状态同步、工业现场控制,DDS 的优势会更明显。
3. 智能指针分别有什么区别
答案:unique_ptr 表示独占所有权,一个对象同一时间只能有一个拥有者,适合资源边界清晰的场景,也是默认优先考虑的智能指针。shared_ptr 表示共享所有权,底层通过控制块维护强引用计数,适合对象会被多个模块共同持有、释放时机难以由单方决定的情况。weak_ptr 不拥有对象,只做观察,不增加强引用计数,主要用于解决 shared_ptr 的循环引用问题。真正工程里,智能指针不是“统一替代裸指针”,而是为了表达所有权。很多时候如果对象根本不具备共享语义,强行上 shared_ptr 反而会让生命周期更乱。
代码:
#include <iostream>
#include <memory>
using namespace std;
struct Node {
int val;
Node(int v) : val(v) {}
~Node() { cout << "~Node\n"; }
};
int main() {
unique_ptr<Node> p1 = make_unique<Node>(1);
shared_ptr<Node> p2 = make_shared<Node>(2);
weak_ptr<Node> p3 = p2;
if (auto sp = p3.lock()) {
cout << sp->val << endl;
}
return 0;
}
4. shared_ptr 的底层实现是什么,多线程下为什么经常提它
答案:shared_ptr 一般由两部分组成,一部分是对象指针,另一部分是控制块。控制块里通常保存强引用计数、弱引用计数、删除器和分配器等信息。每次拷贝 shared_ptr,强引用计数加一;析构或 reset 时减一;强引用归零时对象本体释放,强弱引用都归零时控制块才会释放。多线程下经常提它,是因为它能相对稳妥地管理“对象什么时候还能活着”这件事,避免一个线程还在访问对象,另一个线程已经把对象释放了。但这不代表对象本身线程安全。shared_ptr 只解决生命周期管理,不解决对象内部状态竞争。多个线程同时写对象成员,照样需要锁、原子变量或者别的同步手段。
5. 进程和线程的区别
答案:进程是资源分配的基本单位,线程是 CPU 调度的基本单位。进程有独立地址空间,隔离性更强,一个进程崩了通常不会直接把另一个进程地址空间带坏;线程共享所属进程的大部分资源,比如代码段、堆、全局变量、文件描述符,但每个线程有自己的栈和寄存器上下文。线程切换通常比进程切换轻,但线程之间共享数据更多,出并发问题的概率也更高。如果继续往深里问,一般会追问线程共享哪些资源、切换开销为什么不同、什么时候该用多进程、什么时候该用多线程。
6. 多线程同步互斥一般怎么做
答案:多线程同步的核心目标有两个,一个是互斥访问共享资源,另一个是协调线程之间的执行顺序。最常见的手段是 mutex、shared_mutex、条件变量、原子变量、信号量、自旋锁。如果只是保护一段临界区,mutex 最直接;如果读多写少,可以考虑读写锁;如果需要等待某个条件成立再继续执行,条件变量更自然;如果只是简单状态位或计数器,有时原子变量就够了。真正写多线程代码时,比“会不会用锁”更重要的是先把共享数据边界想清楚,否则锁只会越加越乱。
代码:
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int counter = 0;
mutex mtx;
void work() {
for (int i = 0; i < 100000; ++i) {
lock_guard<mutex> lock(mtx);
++counter;
}
}
int main() {
thread t1(work), t2(work);
t1.join();
t2.join();
cout << counter << endl;
return 0;
}
7. 信号量怎么使用,和互斥锁有什么区别
答案:信号量本质上是一个计数器,用来控制某类资源的可用数量或者线程执行节奏。和互斥锁最大的区别在于,互斥锁通常强调“同一时刻只能一个线程进入临界区”,而信号量强调“同时允许多少个线程通过”或者“某个事件是否已经发
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.


