小米汽车 中间件-C++ 二面
1. make_shared 了解过吗?
答案:了解过。make_shared 本质上是用来创建 shared_ptr 的工厂函数。和直接 shared_ptr<T>(new T(...)) 相比,它最大的特点是通常会把对象本体和控制块一次性分配在同一块内存里,这样能减少一次堆分配,性能和局部性会更好一些。另外它还能避免像 shared_ptr<T>(new T()) 这种写法在复杂表达式里潜在的异常安全问题,所以在工程里一般更推荐优先用 make_shared。不过它也不是完全替代所有场景。比如对象需要自定义删除器,或者对象内存特别大、希望控制块和对象分离管理时,就不一定适合 make_shared。如果对象生命周期很清晰、没有特殊删除需求,make_shared 基本就是更自然的选择。
代码:
#include <iostream>
#include <memory>
using namespace std;
class Node {
public:
Node(int x) : val(x) { cout << "construct\n"; }
~Node() { cout << "destruct\n"; }
private:
int val;
};
int main() {
auto p = make_shared<Node>(10);
cout << p.use_count() << endl;
return 0;
}
2. 模板的优缺点
模板最大的优点是能把类型无关的逻辑抽象出来复用。像容器、迭代器、泛型算法,本质上都依赖模板。这样既减少重复代码,也能在编译期完成类型展开,通常不会引入运行时多态那种额外开销。另外模板还能做很多编译期检查和优化,比如类型推导、特化、SFINAE、概念约束这些,现代 C++ 里很多高质量库都是建立在模板之上的。缺点也很明显。一个是编译报错容易很长,不好定位;一个是模板实例化会带来代码膨胀;再一个是过度使用模板元编程会让代码可读性明显下降,维护成本高。所以模板是很强的工具,但如果只是简单逻辑,没必要为了“泛型”把代码写得过度复杂。
3. static 如何理解,除了内存地址,作用域如何理解?
static 在 C++ 里不能只理解成“放在静态区”。更准确地说,它影响的是存储期、链接属性和归属方式。如果修饰局部变量,它的作用域还是在函数内部,只是生命周期从函数调用期间延长成了整个程序运行期间;如果修饰全局变量或函数,它改变的是链接属性,让这个符号只在当前编译单元可见;如果修饰类成员变量,表示这个成员属于类本身,而不是属于某个对象实例。所以“作用域”和“生命周期”要分开看。局部 static 的作用域没有变,还是局部;变的是存储期。文件作用域下的 static 更像是限制可见范围,避免外部链接污染。
代码:
#include <iostream>
using namespace std;
void func() {
static int cnt = 0; // 作用域仍在 func 内部,生命周期贯穿整个程序
++cnt;
cout << cnt << endl;
}
int main() {
func();
func();
return 0;
}
4. 了解过单例模式吗?
单例模式的核心目标是保证某个类在整个进程里只有一个实例,并且提供全局访问点。C++ 里最常见、也最推荐的是函数内局部静态对象,也就是 Meyers Singleton。因为从 C++11 开始,标准保证了局部静态变量初始化的线程安全。单例模式常见在配置中心、日志器、线程池管理器这类全局唯一资源里,但它也不是没有问题。如果滥用单例,会让模块耦合增强、测试困难、依赖关系不清晰。所以工程里一般只在确实需要全局唯一语义时才用,而不是把它当万能模式。
代码:
#include <iostream>
using namespace std;
class Singleton {
public:
static Singleton& instance() {
static Singleton obj;
return obj;
}
void print() {
cout << "singleton\n";
}
private:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
int main() {
Singleton::instance().print();
return 0;
}
5. 虚函数表指针的地址怎么理解?
通常要先区分虚表在哪里和虚表指针在哪里。虚表本身一般是编译器生成的一张函数指针表,通常放在程序的只读数据区;而虚表指针 vptr 是对象里的一个隐藏成员,它跟着对象走,所以 vptr 本身存放在对象内存布局里。如果对象在栈上创建,那 vptr 就在栈上这块对象内存里;如果对象在堆上创建,那 vptr 就在堆上的对象内存里。它保存的是虚表的首地址或者某种可定位虚表的指针,运行时通过它完成动态绑定。所以“虚函数表指针的地址”本质上是对象内部那个隐藏指针成员所在的位置,而不是说虚表和对象一定在同一个区域。
6. 多态如何理解,或者说有哪几种多态?
多态可以简单理解成:同一个接口,对不同对象表现出不同的行为。C++ 里一般分成两类。一类是编译期多态,比如函数重载
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.
查看7道真题和解析