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。

全部评论
学过一段时间c++,实在是不习惯这种,还是c好
点赞 回复 分享
发布于 今天 12:26 陕西

相关推荐

评论
点赞
收藏
分享

创作者周榜

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