C++八股文(面向对象编程)

1. C++ 中的继承有几种类型?

  • public继承:基类的public成员在派生类中仍是public,protected仍是protected。是"is-a"关系,最常用
  • protected继承:基类的public和protected成员在派生类中都变成protected。外部无法访问
  • private继承:基类的public和protected成员在派生类中都变成private。实现"用...实现"的关系,类似组合
  • 访问规则:无论哪种继承,基类的private成员派生类都不能直接访问
  • 使用建议:90%以上情况用public继承;需要隐藏基类接口时用private继承,但组合通常更好
  • 多重继承:可以从多个基类继承,但要注意菱形继承问题

2. C++ 中如何实现多态?

  • 编译时多态:函数重载、运算符重载、模板。编译期确定调用
  • 运行时多态:通过虚函数实现。三个条件:①基类有虚函数 ②派生类重写虚函数 ③通过基类指针或引用调用
  • 实现机制:每个有虚函数的类有虚函数表(vtable),对象有虚函数指针(vptr)指向vtable。运行时通过vptr查表调用正确的函数
  • 典型应用:接口设计、策略模式、工厂模式等设计模式
  • 性能开销:虚函数调用有间接寻址开销,但通常可以接受
  • 注意事项:基类析构函数应该是虚函数,否则delete基类指针时不会调用派生类析构函数

3. C++ 中的虚函数是什么?

  • 定义:用virtual关键字修饰的成员函数,支持动态绑定
  • 作用:允许派生类重写(override)基类函数,通过基类指针/引用调用时执行派生类版本
  • 语法virtual void func(); 派生类重写时virtual可省略,但建议加override关键字
  • 虚函数表:编译器为每个有虚函数的类生成vtable,存储虚函数地址。对象的vptr指向对应的vtable
  • 构造/析构中的虚函数:构造函数不能是虚函数;析构函数应该是虚函数;构造/析构过程中调用虚函数不会动态绑定
  • 性能:虚函数调用比普通函数多一次间接寻址,通常影响很小
  • final关键字:C++11可用final防止虚函数被进一步重写

4. C++ 中的纯虚函数(Pure Virtual Function)是什么?

  • 定义:在基类中声明但不实现的虚函数,语法:virtual void func() = 0;
  • 作用:定义接口规范,强制派生类必须实现该函数
  • 抽象类:包含纯虚函数的类是抽象类,不能实例化
  • 派生类要求:派生类必须实现所有纯虚函数才能实例化,否则仍是抽象类
  • 可以有实现:纯虚函数可以提供默认实现,派生类可以显式调用
  • 使用场景:定义接口、框架设计、多态基类
  • 示例class Shape { virtual double area() = 0; };

5. C++ 中的抽象类(Abstract Class)是什么?

  • 定义:包含至少一个纯虚函数的类
  • 特点:不能实例化,只能作为基类;可以有数据成员、普通成员函数、构造函数和析构函数
  • 用途:定义接口规范、提供公共实现、实现多态
  • 与接口区别:C++没有interface关键字,抽象类可以有实现和数据成员;纯接口类所有函数都是纯虚函数
  • 指针和引用:可以声明抽象类的指针和引用,指向派生类对象
  • 设计原则:基类应该是抽象的,定义"做什么";派生类是具体的,定义"怎么做"
  • 析构函数:抽象类的析构函数应该是虚函数

6. C++ 中的虚拟继承是什么?

  • 问题背景:菱形继承导致最底层派生类有多份顶层基类的副本,造成二义性和空间浪费
  • 解决方案:用virtual关键字继承,class B : virtual public A,确保只有一份基类副本
  • 实现机制:通过虚基类表(vbtable)和虚基类指针(vbptr)实现,运行时定位唯一的基类子对象
  • 构造顺序:虚基类由最底层派生类直接构造,中间类的构造调用被忽略
  • 性能开销:增加了内存开销(vbptr)和访问开销(间接寻址)
  • 使用场景:解决菱形继承问题,但应该优先考虑组合而非多重继承
  • 注意事项:所有中间类都要用virtual继承才能解决问题

7. 什么是构造函数和析构函数的顺序?

  • 构造顺序:①虚基类(按声明顺序)②直接基类(按声明顺序,不是初始化列表顺序)③成员对象(按声明顺序)④构造函数体
  • 析构顺序:完全相反,①析构函数体 ②成员对象 ③直接基类 ④虚基类
  • 多重继承:按基类在派生类声明中的顺序
  • 初始化列表:成员初始化顺序由声明顺序决定,不是初始化列表中的顺序,要注意依赖关系
  • 异常安全:构造过程中抛异常,已构造的成员会自动析构,但对象本身的析构函数不会调用
  • 虚函数调用:构造/析构过程中调用虚函数不会动态绑定,调用的是当前类的版本
  • 最佳实践:初始化列表顺序应与声明顺序一致,避免混淆

8. C++ 中如何实现运算符重载?

  • 语法返回类型 operator符号(参数列表),如 Complex operator+(const Complex& other)
  • 成员函数形式:左操作数是this,c1 + c2 调用 c1.operator+(c2)
  • 友元函数形式:两个操作数都是参数,c1 + c2 调用 operator+(c1, c2),支持左操作数类型转换
  • 不能重载的运算符::..*?:sizeoftypeid
  • 必须是成员函数=[]()->
  • 常见重载:算术运算符、比较运算符、流运算符(<<>>)、下标运算符、函数调用运算符
  • 返回值:算术运算符返回值,赋值运算符返回引用支持链式调用
  • 最佳实践:保持运算符的直观语义,不要滥用

9. C++ 中如何使用友元函数(Friend Function)?

  • 定义:在类中用friend声明的非成员函数,可以访问类的private和protected成员
  • 声明class MyClass { friend void func(MyClass& obj); };
  • 特点:不是成员函数,没有this指针;破坏封装性,但有时必要
  • 使用场景:①运算符重载(如<<>>需要左操作数是流对象)②需要访问多个类的私有成员 ③提高效率避免getter/setter
  • 友元类friend class OtherClass; 允许另一个类访问私有成员
  • 单向性:A是B的友元,B不自动是A的友元
  • 不传递:A是B的友元,B是C的友元,A不是C的友元
  • 不继承:基类的友元不是派生类的友元
  • 最佳实践:尽量少用,优先考虑public接口

10. 什么是深拷贝与浅拷贝?

  • 浅拷贝:逐位复制对象,指针成员只复制地址值。多个对象共享同一块内存,析构时会重复释放导致崩溃
  • 深拷贝:不仅复制对象,还为指针成员分配新内存并复制内容。每个对象独立管理资源
  • 默认行为:编译器生成的拷贝构造函数和赋值运算符执行浅拷贝
  • 何时需要深拷贝:类中有指针成员、动态分配资源、文件句柄等需要独立管理的资源
  • 三五法则:需要自定义析构函数,通常也需要自定义拷贝构造函数和拷贝赋值运算符(C++11还包括移动构造和移动赋值)
  • 实现要点:拷贝赋值要处理自赋值、释放旧资源、分配新资源、复制内容
  • 现代方案:使用智能指针(shared_ptr、unique_ptr)自动管理资源,避免手动深拷贝
// 深拷贝示例
class String {
    char* data;
public:
    // 深拷贝构造
    String(const String& other) {
        data = new char[strlen(other.data) + 1];
        strcpy(data, other.data);
    }
    // 深拷贝赋值
    String& operator=(const String& other) {
        if (this != &other) {  // 防止自赋值
            delete[] data;
            data = new char[strlen(other.data) + 1];
            strcpy(data, other.data);
        }
        return *this;
    }
    ~String() { delete[] data; }
};

C++八股文全集 文章被收录于专栏

本专栏系统梳理C++技术面试核心考点,涵盖语言基础、面向对象、内存管理、STL容器、模板编程及经典算法。从引用指针、虚函数表、智能指针等底层原理,到继承多态、运算符重载等OOP特性从const、static、inline等关键字辨析,到动态规划、KMP算法、并查集等手写实现。每个知识点以面试答题形式呈现,注重原理阐述而非冗长代码,帮助你快速构建完整知识体系,从容应对面试官提问,顺利拿下offer。

全部评论

相关推荐

迷茫的大四🐶:不是,匿名发帖,你也可以发
点赞 评论 收藏
分享
2025-12-24 02:41
哈尔滨工业大学 Java
又是焦虑的一个晚上,不想投了不想面了,想静下心来好好沉淀,也是给自己喘口气的机会。在各个平台焦虑地刷来刷去,想了想还是过来写一下从十月以来五次面试的收获。十月:可以说是惨不忍睹。第一次面的小厂,基本没有答出来的问题,当时光顾着看大家的面经背一些高频八股,却连最基本的东西都忘光了。第二次面的滴滴,这次的八股是最最常见的一些,JVM、垃圾回收、隔离级别,算法也是mid,可惜这个机会没有抓住,当时hot100只刷了30,甚至很多语法都不熟。面完自欺欺人沉淀了两个月,实际上这两个月八股一点没背,算法倒是进步了一些。十二月考完试开始海投,明显比十月份时约面难多了。小红书:也很可惜,感觉和面试官聊得不错,问了我很多我做过的ai方面的东西,但是组内主要是做客户端的,反复确认了好几遍我一点都不了解客户端吗。还有最后问了一些Java基础的问题,也是挺好笑,我依旧是一点不知道,太浮躁,Java基础的数据结构的八股一点都没背,很多东西看着差不多就放过去。美团:史上最难堪的一次,详见主页。项目、八股都问到了很多很多我的盲点,可以当错题集用了(笑。面完彻底崩溃。一直都在自欺欺人,实际上就是什么也不会。小厂:莫名其妙。两人在会议间里一起问我。问了两个智力题,都答上来了,依旧挂。可能也是我八股答得不好。这个厂是做类似交易平台的,所以他也问了挺多数据库的事,深挖了一下项目,如果超卖一人一单不用redis而是用MySQL的锁怎么解决,完全愣住了,没准备过,MySQL只会背八股和最简单的查询语句。还发现这个Java基础的数据结构依旧是有问题。哈哈,迄今为止没过过一面,没心力也没胆量继续投了。这一两周把学校的实验、期末收尾也确实挺忙的,回家之后随缘投一投吧,学校里面试太不方便了。刚刚把八股来回翻了翻,想想自己有多少不会,想想自己有多少欠缺,把投的一份简历撤回了。每次投完简历都会做噩梦、被约面试也会做噩梦,给自己一段时间休整,回家以后全力梭哈,希望来年能有好结果。
点赞 评论 收藏
分享
翻了翻日历,我已经入职云智交互设计岗的一年有余啦。坐在熟悉的工位上,看着屏幕上正在打磨的产品原型,心中满是感慨——这一年在每一次原型迭代、每一轮用户反馈中,逐渐熟悉和掌握了交互设计的核心技能,感觉自己一步步从“职场小白”成长为一个能独立承接需求的设计师。回想起来初入职场的第一个月,我陷入了“理论与实践脱节”的困境。大学时学过交互设计的基础理论,但真正接手具体的客户需求时却犯了难,很容易陷入“只考虑了美观,没考虑用户使用场景”、“关键操作按钮层级不清晰,用户找不到核心功能”、“忽略了年龄较大用户的视觉适配需求”等等;在一次次的迭代优化中我才意识到,交互设计不是“画好看的界面”,而是“让用户好用、易用”。这一年多我也成长了很多,我开始理解“以用户为中心”的真正含义,画原型时会主动考虑用户的操作习惯、视觉焦点和使用场景。当我的设计方案,通过了内部评审且收获正向的用户反馈时,我真切感受到技能的提升需要靠扎实的练习和精准的复盘。除了专业技能的提升,这一年里我最大的收获是还有心态的成熟。从一开始遇到问题就慌慌张张、急于求助,到现在能够沉着冷静地分析问题、主动寻找解决方案;从一开始害怕犯错、不敢尝试,到现在明白“犯错不可怕,可怕的是不敢直面错误”,只有不断在错误中反思、在反思中进步,才能真正成长。如今,我已经能够独立承接中小型功能的交互设计需求,从需求分析、用户研究到原型设计、测试复盘,整个流程都能顺畅推进。未来的日子里,我依然会带着这份初心和热爱,在工作中继续沉淀自己、突破自己。我相信,只要保持向上的姿态,每一步前行都将充满意义,每一次努力都将收获成长。感谢这一年的相遇与陪伴,也感谢那个咬牙坚持、从未放弃的自己。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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