C++ 服务器开发面试题整理(2)
1.虚函数怎样实现的
(1)虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的,虚函数表简称为V-Table。
(2)在这个表中存放的主要是一个类的虚函数的地址表。虚函数表中可能还存在其他的内容,如用于RTTI的type_info类型,或者直接将虚基类的指针存放在虚表中。
(3)C++的编译器应该保证虚函数表的指针存在于对象实例中最前面的位置,这样可以保证最快的取虚函数表效率。
2.C++怎样实现多态
(1)静态多态通过函数重载和模板实现,编译时确定。
(2)动态多态通过虚函数和继承关系来实现,执行动态绑定,在运行的时候确定。派生类对象的指针或引用可以绑定在基类对象上。在调用虚函数时,就会去查找该对象的虚函数表。查找该虚函数表中该函数的指针进行调用。
3.虚函数表(整理自https://blog.csdn.net/haoel/article/details/1948051)
(1)如果派生类没有重写子类的虚函数,并且又定义了三个虚函数:
则派生类虚表内容为:
上图显示,虚函数按照其声明顺序放于表中且父类的虚函数在子类的虚函数前面。
(2)如果派生类重写了部分子类虚函数,下表中只重写了子类的虚函数f,之后又定义了两个虚函数g1、h1:
则虚表内容为:
我们看到派生类中用来重写基类虚函数的虚函数被放到了基类的被覆盖的虚函数的位置,而其他虚函数排列不变。
(3)多重继承且派生类未重写任何基类虚函数,且派生类中又定义了两个新虚函数:
则虚表内容为:
可以看到派生类的虚函数被放到了第一个基类的虚表中。
(4)多重继承且派生类重写了基类虚函数:
则虚表内容为:
可以看到三个基类虚函数表中f的位置被替换为了派生类重写的虚函数。
(5)其他知识
1)C++内部为每一个类维持一个虚函数表,该类的所有对象都指向同一个虚函数表。
2)通过虚函数表可以访问子类private的虚函数:
#include <iostream>
using namespace std;
class Base {
private:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
};
class Derive : public Base { };
typedef void(*Fun)(void);
int main() {
Derive d;
Fun pFun = (Fun)*((int*)*(int*)&d + 1); // pFun指向基类虚函数g
pFun();
}
取子类private虚函数过程:
1.&d
:取派生类对象d的首地址,虚函数表通常放在对象的首地址处。
2.(int*)&d
:将首地址转换为int型指针,转换为int型指针的原因是指针可以和整型和其他指针类型相互转换,而不能和float等浮点数类型相互转换。
3.*(int*)&d
:此时取地址相当于取了虚表的前四个字节,并强制使这四个字节被编译器识别为int。
4.(int*)*(int*)&d
:将int类型(实际是虚表中的前4个字节)转换为指针类型(这也是为什么第2步需要转换int指针,因为此处又将int转换为了指针),因为虚函数表中存放的本就是指向虚函数的指针。
5.(int*)*(int*)&d + 1
:取令int型指针指向第二个虚函数,+1使指针指向虚函数表中的下一个函数。
6.(Fun)*((int*)*(int*)&d + 1)
:将int型指针转换为函数指针类型。
4.为什么有虚函数的类的析构函数要设计成虚函数?
当基类指针指向在堆上的派生类对象时,如此时使用delete,如果基类的析构函数不是虚函数,则会调用基类的析构函数析构派生类对象。因此需要将析构函数定义成虚函数,以在析构时调用对应类的析构函数。
5.析构函数能抛出异常吗?
不能。
(1)如果析构函数抛出异常,则异常点之后的程序不会执行,如果析构函数在异常点之后执行了某些必要的动作比如释放某些资源,则这些动作不会执行,会造成诸如资源泄漏的问题。
(2)通常异常发生时,c++会进行栈展开来寻找异常的catch语句,如果在当前函数中未找到对应catch,会将退出当前函数,退出时会销毁函数栈中的对象,如有类类型对象,则调用其析构函数销毁它,此时若析构函数本身也抛出异常,则前一个异常尚未处理,又有新的异常,会造成程序崩溃的问题。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)