恒生电子 C++开发 一面

1. 讲一下虚函数表,以及虚函数什么时候生成的?构造函数可以被定义为虚函数吗?

答案:C++ 里带虚函数的类,编译器通常会为它生成一张虚函数表,表里存放的是该类各个虚函数最终对应的函数入口地址。对象实例里一般还会有一个隐藏的虚表指针,用来在运行时找到这张表。通过基类指针或引用调用虚函数时,程序会先取出对象里的虚表指针,再间接找到真正要执行的函数,这就是运行时多态的基础。虚函数表是在编译期由编译器根据类定义生成的,不是运行时动态“创建”的。真正到运行时参与的是对象里的虚表指针初始化过程。对象构造时,编译器会在不同构造阶段把 vptr 指向当前构造层级对应的虚表,所以构造和析构期间的虚函数行为要特别小心。构造函数不能声明为虚函数。因为调用构造函数的前提恰恰是对象还没有构造完成,这时对象的多态基础都还没稳定建立,拿“虚调用”去决定怎么构造本身在语义上就是矛盾的。析构函数则经常要声明为虚函数,尤其是当类要作为基类被多态删除时。

代码:

#include <iostream>
using namespace std;

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

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

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

2. protected 的作用是什么

protected 主要用于控制继承体系内的访问边界。一个成员如果是 protected,那么类外部不能直接访问,但派生类内部可以访问。它比 private 更开放一些,比 public 又更收敛一些。它常见的用途不是“偷懒不给封装”,而是当基类希望给子类暴露一部分实现细节或扩展点,但又不希望这部分能力直接暴露给所有外部调用方。实际工程里如果一个成员只想让子类感知,protected 是合理的;但如果会破坏基类不变量,通常还是应该优先通过 protected 的辅助函数或者纯虚接口来暴露,而不是直接把数据成员裸露出来。

代码:

#include <iostream>
using namespace std;

class Base {
protected:
    int value = 42;
};

class Derived : public Base {
public:
    void print() { cout << value << endl; }
};

int main() {
    Derived d;
    d.print();
    return 0;
}

3. 函数传参和返回值一般走哪些寄存器,参数是怎么压栈的?

在 Linux x86-64 下,常见的 System V ABI 规定,整数或指针类型的前六个参数通常通过寄存器传递,分别是 rdirsirdxrcxr8r9,返回值一般走 rax。如果参数更多,超出的部分才会放到栈上。浮点参数则会优先走 xmm 系列寄存器。在 Windows x64 下,前四个整型或指针参数通常放在 rcxrdxr8r9。所以面试里如果被问到“哪个寄存器传参”,最好先说明你讨论的是哪种平台和 ABI。至于“如何压栈”,现代编译器不一定真的是传统意义上一条条 push,很多时候会在函数序言中一次性调整 rsp,然后把参数或临时变量放到预留好的栈帧位置。编译优化打开后,很多小函数甚至可能完全不建传统栈帧。

代码:

#include <iostream>
using namespace std;

long add(long a, long b, long c, long d, long e, long f, long g) {
    return a + b + c + d + e + f + g;
}

int main() {
    cout << add(1,2,3,4,5,6,7) << endl;
    return 0;
}

4. 讲一下进程地址空间

答案:进程地址空间是操作系统给每个进程提供的一套独立虚拟地址视图。程序看到的是一段看起来连续的虚拟地址,而不是直接操作物理内存。典型的进程地址空间里会包括代码段、只读常量区、全局/静态区、堆、共享库映射区、文件映射区、栈,以及内核为线程和运行时保留的一些特殊区域。不同平台和运行时布局细节不完全一样,但核心思想都是一致的。有了地址空间隔离,不同进程之间默认不能随便互相读写,这既提高了安全性,也降低了错误传播范围。与此同时,虚拟内存还支持按需分页、共享库映射、写时复制、内存映射文件等机制。所以“进程地址空间”不是简单的一块内存,而是操作系统、MMU、页表、权限位共同提供的一层抽象。

5. C++ 函数调用的几种方式有哪些

答案:如果从“调用形态”来看,常见有普通函数调用、成员函数调用、虚函数调用、函数指针调用、仿函数调用、lambda 调用。如果从更底层的“调用约定”来看,常见会提到 cdeclstdcallfastcallthiscall 等,不过在 64 位平台上很多历史差异被统一 ABI 吸收掉了。成员函数本质上会多一个隐式的 this 指针;虚函数调用会多一层经由虚表的间接跳转;函数对象和 lambda 通常会转化成对 operator() 的调用。所以这题如果面试官问得偏底层,你就往 ABI、寄存器、栈帧、this 指针说;如果问得偏语言特性,就往普通函数、成员函数、虚函数、可调用对象这几种形式说。

代码:

#include <iostream>
using namespace std;

void normalFunc() { cout << "normal\n"; }

class A {
public:
    void memberFunc() { cout << "member\n"; }
    virtual void virtualFunc() { cout << "virtual\n"; }
};

struct Functor {
    void operator()() { cout << "functor\n"; }
};

int main() {
    normalFunc();

    A a;
    a.memberFunc();
    a.v

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

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

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

全部评论

相关推荐

评论
点赞
2
分享

创作者周榜

更多
正在热议
更多
# 春招至今,你的战绩如何? #
12545次浏览 110人参与
# 军工所铁饭碗 vs 互联网高薪资,你会选谁 #
7825次浏览 43人参与
# 米连集团26产品管培生项目 #
6582次浏览 221人参与
# 你的实习产出是真实的还是包装的? #
2222次浏览 44人参与
# 简历第一个项目做什么 #
31885次浏览 348人参与
# 长得好看会提高面试通过率吗? #
1329次浏览 27人参与
# 巨人网络春招 #
11414次浏览 224人参与
# 重来一次,我还会选择这个专业吗 #
433699次浏览 3926人参与
# 当下环境,你会继续卷互联网,还是看其他行业机会 #
187412次浏览 1123人参与
# 牛客AI文生图 #
21472次浏览 238人参与
# 不考虑薪资和职业,你最想做什么工作呢? #
152662次浏览 888人参与
# 研究所笔面经互助 #
119008次浏览 577人参与
# 简历中的项目经历要怎么写? #
310640次浏览 4237人参与
# AI时代,哪些岗位最容易被淘汰 #
64211次浏览 847人参与
# 面试紧张时你会有什么表现? #
30537次浏览 188人参与
# 你今年的平均薪资是多少? #
213352次浏览 1039人参与
# 你怎么看待AI面试 #
180408次浏览 1277人参与
# 高学历就一定能找到好工作吗? #
64353次浏览 620人参与
# 你最满意的offer薪资是哪家公司? #
76759次浏览 374人参与
# 我的求职精神状态 #
448259次浏览 3129人参与
# 正在春招的你,也参与了去年秋招吗? #
363858次浏览 2638人参与
# 腾讯音乐求职进展汇总 #
160747次浏览 1114人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务