金山世游 客户端开发-C++ 一面

1. 自我介绍

2. 如何理解继承、多态和封装,在什么场景下使用

答案:封装的核心是把数据和行为收进对象内部,对外只暴露稳定接口,减少外部对实现细节的依赖。继承主要解决“共性抽取”和“接口复用”的问题,但它不应该被滥用成单纯的代码复用手段。多态的意义在于让同一套调用代码适配不同实现,比较适合行为抽象稳定、具体实现可扩展的场景,比如 UI 控件基类、资源加载器接口、渲染后端抽象、事件处理器体系。真正工程里,能不能用好多态,不在于会不会写 virtual,而在于抽象边界是否稳定。如果层次设计得不好,后面会很快出现基类臃肿、子类职责混乱的问题。

代码:

#include <iostream>
using namespace std;

class Widget {
public:
    virtual void draw() = 0;
    virtual ~Widget() = default;
};

class Button : public Widget {
public:
    void draw() override {
        cout << "draw button\n";
    }
};

class Slider : public Widget {
public:
    void draw() override {
        cout << "draw slider\n";
    }
};

int main() {
    Widget* w1 = new Button();
    Widget* w2 = new Slider();
    w1->draw();
    w2->draw();
    delete w1;
    delete w2;
    return 0;
}

3. 智能指针介绍一下,shared_ptr 在什么场景下使用

答案:智能指针本质上是把资源管理和对象生命周期绑定在一起,避免手动释放时遗漏。unique_ptr 表示独占所有权,适合所有权清晰的资源;shared_ptr 表示共享所有权,适合确实存在多个持有者、对象退出时机无法由单一模块决定的场景;weak_ptr 用来观察 shared_ptr 管理的对象,避免循环引用。shared_ptr 常见于节点图、资源缓存、回调系统、异步任务对象这些“生命周期跨多个模块”的场景。但它不是默认首选,如果对象所有权本来就清楚,优先还是 unique_ptr 或者值对象。shared_ptr 用多了,往往说明对象关系没有设计干净。

4. shared_ptr 的引用计数是怎么实现的,多线程下要注意什么

答案:shared_ptr 一般由对象指针和控制块组成,控制块里维护强引用计数、弱引用计数、删除器、分配器等信息。每次拷贝 shared_ptr,强引用计数加一;销毁时减一;减到零时析构对象;控制块通常要等弱引用也归零才真正释放。多线程下要注意两层问题:一层是引用计数自身的线程安全,这通常依赖原子操作;另一层是对象内容本身的线程安全,这个 shared_ptr 并不能保证。也就是说,多个线程同时持有同一个 shared_ptr 通常没问题,但如果大家同时修改对象内部状态,依然要靠锁、原子变量或者更清晰的并发设计来保护。

5. 数组和 map 的区别,vector 的扩容机制是怎样的

答案:数组是固定大小、连续内存,访问方式简单,随机访问快;map 是关联容器,按 key 存储,底层通常是红黑树,支持有序查找和范围操作。vector 也是连续内存,但支持动态扩容。它容量不足时会申请一块更大的新内存,把原有元素搬迁过去,再释放旧内存。扩容倍率标准没有强制统一实现,但常见是按 1.5 倍或者 2 倍增长。一旦扩容发生,原有的指针、引用、迭代器一般都会失效,所以写客户端或游戏代码时,如果对象地址稳定性有要求,就不能无脑把对象指针长期绑在 vector 元素地址上。

代码:

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

int main() {
    vector<int> v;
    size_t last_cap = v.capacity();

    for (int i = 0; i < 20; ++i) {
        v.push_back(i);
        if (v.capacity() != last_cap) {
            cout << "size=" << v.size() << ", new capacity=" << v.capacity() << endl;
            last_cap = v.capacity();
        }
    }
    return 0;
}

6. 除了 map,你怎么比较 mapunordered_map

答案:map 底层通常是红黑树,元素有序,查找、插入、删除复杂度稳定在 O(logn),适合需要范围查询、有序遍历、上下界查找的场景。unordered_map 底层是哈希表,平均查找复杂度接近 O(1),适合高频按 key 查找,但不保证有序,而且要关注哈希冲突、rehash、负载因子和自定义哈希质量。如果业务里 key 分布比较均匀、只关心单点查找,unordered_map 通常更快;如果需要稳定顺序、区间操作或对最坏情况更敏感,map 更合适。游戏客户端里两者都很常见,关键还是看访问模式,而不是只背复杂度。

7. 了解哪些游戏引擎,它们的核心差异一般体现在哪

答案:常见像 Unity、Unreal,还有一些公司内部自研引擎。如果从客户端开发角度看,核心差异一般体现在脚本系统、渲染管线、资源管理、编辑器能力、平台支持、热更新体系和工具链成熟度。Unity 更强调组件式开发和编辑器工作流,C# 上层开发效率高;Unreal 在渲染、蓝图系统、C++ 深度定制和大型项目工业化上更

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

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

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

全部评论

相关推荐

03-03 19:02
已编辑
东华理工大学 Node.js
点赞 评论 收藏
分享
03-10 22:53
吉林大学 golang
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
正在热议
更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务