C++面向对象编程面试题总结
1. C++中的三大特性是什么?
答案:
- 封装(Encapsulation)将数据和操作数据的方法绑定在一起通过访问控制符(public、private、protected)隐藏内部实现只暴露必要的接口给外部提高代码安全性和可维护性
- 继承(Inheritance)子类继承父类的属性和方法实现代码复用建立类之间的层次关系支持单继承和多继承
- 多态(Polymorphism)同一接口,不同实现编译时多态:函数重载、运算符重载运行时多态:虚函数、纯虚函数提高代码灵活性和扩展性
2. 什么是虚函数?什么是纯虚函数?
答案:
- 虚函数用virtual关键字声明:virtual void func();允许子类重写(override)通过基类指针/引用调用时,执行实际对象的函数实现运行时多态
- 纯虚函数在声明后加= 0:virtual void func() = 0;没有实现,必须由子类实现包含纯虚函数的类是抽象类,不能实例化定义接口规范
- 虚函数表(vtable)每个包含虚函数的类都有一个虚函数表存储虚函数的地址对象中有一个虚函数表指针(vptr)运行时通过vptr查找正确的函数
- 使用场景需要多态行为时使用虚函数定义接口时使用纯虚函数析构函数通常声明为虚函数
3. 构造函数和析构函数的调用顺序是什么?
答案:
- 单个对象构造:先调用构造函数,再执行函数体析构:先执行函数体,再调用析构函数
- 继承关系构造顺序:基类构造 → 成员对象构造 → 派生类构造析构顺序:派生类析构 → 成员对象析构 → 基类析构析构顺序与构造顺序相反
- 多继承按照继承声明的顺序构造基类析构顺序相反
- 成员对象按照成员变量声明的顺序构造与初始化列表顺序无关析构顺序相反
4. 什么是拷贝构造函数?什么时候会调用?
答案:
- 拷贝构造函数定义用一个对象初始化另一个对象签名:ClassName(const ClassName& other);如果不定义,编译器会生成默认版本(浅拷贝)
- 调用时机用一个对象初始化另一个对象:A a2 = a1;函数参数按值传递:void func(A obj);函数返回对象(可能被优化):A func() { return a; }容器插入元素时
- 深拷贝vs浅拷贝浅拷贝:只复制指针值,共享资源深拷贝:复制指针指向的内容,独立资源包含指针成员时必须实现深拷贝
- 三五法则如果需要自定义析构函数,通常也需要自定义拷贝构造和拷贝赋值C++11后扩展为五法则:加上移动构造和移动赋值
5. 什么是移动构造函数?为什么需要它?
答案:
- 移动构造函数定义C++11引入签名:ClassName(ClassName&& other) noexcept;转移资源所有权,而非复制
- 为什么需要避免不必要的深拷贝提高性能,特别是大对象支持只能移动不能拷贝的类型(如unique_ptr)
- 实现要点转移资源指针将源对象置为安全状态(nullptr)标记为noexcept,提高容器性能
- 调用时机用右值初始化对象:A a2 = std::move(a1);函数返回临时对象容器扩容时移动元素
6. 什么是运算符重载?有哪些注意事项?
答案:
- 运算符重载定义为自定义类型定义运算符行为使用operator关键字:operator+可以是成员函数或友元函数
- 可重载的运算符算术运算符:+、-、*、/、%关系运算符:==、!=、<、>、<=、>=逻辑运算符:&&、||、!位运算符:&、|、^、~、<<、>>赋值运算符:=、+=、-=等其他:[]、()、->、new、delete
- 不可重载的运算符::(作用域解析).(成员访问).*(成员指针访问)?:(三元运算符)sizeof、typeid
- 注意事项保持运算符原有语义注意返回类型(值、引用、const)赋值运算符返回*this引用前置++返回引用,后置++返回值某些运算符必须是成员函数(=、[]、()、->)
7. 什么是友元函数和友元类?
答案:
- 友元函数用friend关键字声明不是类的成员函数,但可以访问私有成员常用于运算符重载:friend ostream& operator<<(ostream&, const A&);
- 友元类一个类的所有成员函数都是另一个类的友元声明:friend class B;B类可以访问A类的所有成员
- 特点破坏封装性,谨慎使用友元关系不能继承友元关系不具有传递性友元关系是单向的
- 使用场景运算符重载(特别是<<、>>)需要访问多个类私有成员的函数紧密相关的类之间
8. 什么是多继承?有什么问题?
答案:
- 多继承定义一个类继承多个基类语法:class C : public A, public B {};
- 菱形继承问题D继承B和C,B和C都继承AD中有两份A的成员造成二义性和内存浪费
- 虚继承解决方案使用virtual关键字:class B : virtual public A {};保证只有一份基类成员最派生类负责初始化虚基类
- 多继承的问题增加复杂性可能的命名冲突构造和析构顺序复杂建议优先使用组合而非多继承
9. public、protected、private继承的区别?
答案:
- public继承基类的public成员在派生类中仍是public基类的protected成员在派生类中仍是protected基类的private成员不可访问表示"is-a"关系
- protected继承基类的public和protected成员在派生类中都变为protected基类的private成员不可访问外部无法通过派生类访问基类接口
- private继承基类的public和protected成员在派生类中都变为private基类的private成员不可访问表示"用...实现"关系,类似组合
- 使用建议绝大多数情况使用public继承private继承可用组合代替protected继承很少使用
10. 什么是抽象类和接口?
答案:
- 抽象类包含至少一个纯虚函数的类不能实例化对象可以有数据成员和普通成员函数可以有构造函数和析构函数
- 接口(C++中的实现)只包含纯虚函数的抽象类没有数据成员所有成员函数都是public定义规范和契约
- 使用场景定义通用接口强制派生类实现特定功能实现多态解耦合,面向接口编程
- 示例
哔哩哔哩公司氛围 762人发布