品峰医疗 客户端开发-C++ 一面

1. C++ 多态的实现原理是什么,静态多态和动态多态有什么区别

C++ 多态分成静态多态和动态多态。静态多态一般发生在编译期,比如函数重载、模板实例化;动态多态一般依赖继承和虚函数,在运行期决定调用哪个函数。动态多态的核心是虚函数表。一个含有虚函数的类,对象内部通常会维护一个虚表指针,指向该类型对应的虚函数表。通过基类指针或引用调用虚函数时,会根据对象真实类型找到实际要执行的函数。静态多态没有运行时分派开销,性能更直接;动态多态扩展性更好,适合接口抽象和运行时替换。工程上如果类型关系在编译期就确定,模板往往更高效;如果要面向接口编程,动态多态更常见。

代码:

#include <iostream>
using namespace std;

class Base {
public:
    virtual void run() { cout << "Base\n"; }
    virtual ~Base() = default;
};

class Derived : public Base {
public:
    void run() override { cout << "Derived\n"; }
};

int main() {
    Base* p = new Derived();
    p->run();
    delete p;
    return 0;
}

2. 计算机里整数、负数和浮点数分别是怎么存的

无符号整数通常直接按二进制存储。带符号整数主流机器上一般采用补码表示,这样加减法可以统一交给硬件处理。补码里,正数的补码和原码一致,负数的补码等于其绝对值按位取反再加一,所以 -1 在 32 位机器上通常是所有位都为 1。浮点数通常采用 IEEE 754 标准表示,分成符号位、指数位和尾数位。以 float 为例,一般是 1 位符号位、8 位指数位、23 位尾数位。浮点数不是精确表示所有十进制小数,因此会存在精度误差。面试里如果被继续追问,可以进一步说浮点数为什么不能直接比较相等,以及 NaN、无穷大、非规格化数这些特殊值。

代码:

#include <iostream>
using namespace std;

int main() {
    int x = -1;
    float y = 0.1f;
    cout << x << endl;
    cout << y << endl;
    return 0;
}

3. 具体讲一下虚函数和虚函数表,虚表一般放在哪里

虚函数是为了支持运行时多态。编译器通常会给含有虚函数的类生成一张虚函数表,表中存放该类各个虚函数的入口地址。对象里会额外有一个隐藏的虚表指针,构造对象时由编译器负责初始化。当通过基类指针调用虚函数时,程序会先找到对象中的虚表指针,再到虚表中取出对应函数地址进行调用。虚函数代码本身在代码段,虚表通常放在只读数据区或者类似区域,对象实例中通常只存一个指针。如果涉及多继承、虚继承,对象布局会更复杂,因为编译器还要处理基类偏移和 this 指针调整。

4. 智能指针有哪些,什么情况下不能随便用智能指针

答案:常见智能指针有 unique_ptrshared_ptrweak_ptrunique_ptr 表示独占所有权,开销小;shared_ptr 表示共享所有权,内部通常维护引用计数;weak_ptr 不拥有对象,主要用于观察和打破循环引用。并不是任何地方都适合无脑上智能指针。比如对象所有权很明确、生命周期跟栈绑定时,直接用局部对象更简单;性能敏感路径上大量使用 shared_ptr,可能引入额外计数开销;跨模块边界乱传 shared_ptr,容易让生命周期变得模糊。还有一种典型情况是“对象并不真正拥有资源”,这时用智能指针管理反而会制造错误的释放语义。所以本质上不是“能不能用智能指针”,而是要先想清楚所有权。

代码:

#include <iostream>
#include <memory>
using namespace std;

class A {
public:
    A() { cout << "ctor\n"; }
    ~A() { cout << "dtor\n"; }
};

int main() {
    unique_ptr<A> p1 = make_unique<A>();
    shared_ptr<A> p2 = make_shared<A>();
    weak_ptr<A> p3 = p2;
    return 0;
}

5. vector 的扩容机制是什么,为什么频繁 push_back 可能带来性能问题

答案:vector 底层是连续内存,当容量不足时会申请一块更大的连续空间,把旧元素搬过去,再释放旧空间。扩容倍数不是标准强制规定,常见实现可能是 1.5 倍或 2 倍。由于扩容时要重新分配内存并拷贝或移动已有元素,所以在元素很多、对象很重时,扩容开销会比较明显。频繁 push_back 的问题主要在于如果提前不知道数据量,就可能触发多次扩容;而且连续内存要求较高,大对象或者内存碎片严重时也可能影响分配效率。工程里如果能预估元素数量,通常会先 reserve,这样可以显著减少重分配次数。

代码:

#include <vector>
using namespace std;

int main() {
    vector<int> v;
    v.reserve(1000);
    for (int i = 0; i < 1000; ++i) v.push_back(i);
    return 0;
}

6. 快速排序什么时候会退化成 O(n^2),常见优化手段有哪些

答案:快速排序在每次划分都极不均衡时会退化成 O(n^2),典型情况是数组本身接近有序,而每次又总是选第一个或最后一个元素作为 pivot。常见优化方式包括随机选 pivot、三数取中、当分区规模较小时切换为插入排序,以及递归时优先处理较小区间来降低栈深度。如果要进一步控制最坏情况,一些标准库实现会采用 introsort,也就是先用快排,当递归深度过深时切到堆排序,从而把最坏情况控制住。所以面试里只说“快排平均 O(nlogn)”是不够的,更重要的是你知不知道它为什么会退化、怎么做工程优化。

代码:

#include <cstdlib>

int partition(int a[], int l, int r) {
    int idx = l + rand() % (r - l + 1);
    int pivot = a[idx];
    

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

C++ 常考面试题总结 文章被收录于专栏

本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.

全部评论

相关推荐

03-17 23:54
黑龙江大学 Java
来个白菜也好啊qaq:可以的,大厂有的缺打手
点赞 评论 收藏
分享
评论
点赞
2
分享

创作者周榜

更多
正在热议
更多
# 春招至今,你的战绩如何? #
10814次浏览 93人参与
# 你的实习产出是真实的还是包装的? #
1927次浏览 42人参与
# 米连集团26产品管培生项目 #
5991次浏览 216人参与
# 军工所铁饭碗 vs 互联网高薪资,你会选谁 #
7603次浏览 43人参与
# 简历第一个项目做什么 #
31715次浏览 338人参与
# 重来一次,我还会选择这个专业吗 #
433492次浏览 3926人参与
# MiniMax求职进展汇总 #
24078次浏览 309人参与
# 当下环境,你会继续卷互联网,还是看其他行业机会 #
187168次浏览 1122人参与
# 牛客AI文生图 #
21442次浏览 238人参与
# 不考虑薪资和职业,你最想做什么工作呢? #
152405次浏览 888人参与
# 研究所笔面经互助 #
118936次浏览 577人参与
# 简历中的项目经历要怎么写? #
310278次浏览 4216人参与
# AI时代,哪些岗位最容易被淘汰 #
63695次浏览 826人参与
# 面试紧张时你会有什么表现? #
30507次浏览 188人参与
# 你今年的平均薪资是多少? #
213102次浏览 1039人参与
# 你怎么看待AI面试 #
180077次浏览 1258人参与
# 高学历就一定能找到好工作吗? #
64329次浏览 620人参与
# 你最满意的offer薪资是哪家公司? #
76509次浏览 374人参与
# 我的求职精神状态 #
448101次浏览 3129人参与
# 正在春招的你,也参与了去年秋招吗? #
363434次浏览 2638人参与
# 腾讯音乐求职进展汇总 #
160658次浏览 1112人参与
# 校招笔试 #
471033次浏览 2964人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务