多态的原理

有虚函数的类中存在一个虚函数表指针,这张表简称虚表,表中存虚函数的地址。在继承体系中,派生类会将基类的虚表继承下来,当派生类没有重写基类虚函数时,派生类和基类虚表中的虚函数地址是一样的,当派生类重写了基类的虚函数时,派生类会用新的虚函数地址覆盖基类的虚函数地址。

调用虚函数时,派生类会通过切片的方式将基类的那一部分切出来(包含派生类的虚表)。这样,基类指针指向派生类对象,就调用派生类虚表中的的虚函数,指向基类,就调用基类虚表中的的虚函数。

运行时多态和编译时多态

运行时多态(动态多态/动态绑定):

通过继承和虚函数来实现,是指在程序运行时,到对象虚函数表指针指向的虚表中找对应的函数地址进行调用,在运行时才知道调用谁。

编译时多态(静态多态/静态绑定):

通过函数重载和运算符重载实现,是指在程序编译阶段,根据函数名来确定调用函数的地址,编译时就知道调用谁了。

为什么在继承体系中最好把析构函数定义成虚函数

若不写成虚函数:

基类指针指向子类对象时,析构函数就是普通的函数调用,指针是父类的就会调用父类的析构函数,但子类的那一部分并没有析构,可能会内存泄漏。

将析构写成虚函数:

子类析构函数完成对父类析构函数的重写,通过父类指针调用析构函数时,满足多态语法。指针指向父类就调用父类的析构,指向子类就调用子类的析构,不会存在内存泄漏的风险。

虚函数可以是内联/构造/静态函数吗

1.虚函数不可以是内联函数,内联函数在调用的地方展开,没有地址,不能存入虚表。

2.虚函数不可以是静态函数,因为静态函数没有this指针,而虚函数的调用恰好需要this指针访问虚表。

3.虚函数不可以是构造函数,因为对象中的虚表是在构造函数走初始化列表时才初始化的。

重载/重写(覆盖)/重定义(隐藏)

重载是两个函数在同一作用域中,函数名相同但参数不同。

重写就是在继承体系中,派生类虚函数完成对基类虚函数的重写。

重定义是在继承体系中,派生类和基类的两个函数名相同,但不构成重写,那么就是隐藏。

什么是哈希冲突?怎么解决?

哈希冲突:

哈希冲突就是两个不同的关键字通过同一个哈希函数计算出相同的结果,从而映射到哈希结构的相同位置。

解决方案:

通常使用闭散列(开放定址法)或开散列(链地址法)解决哈希冲突。

(1)闭散列:当发生哈希冲突时,若负载因子较小(一般不超过0.5),从冲突位置开始向后探测,找空闲位置插入。

(2)开散列:通过哈希函数算出相同结果的数据放在同一个桶中,每个桶中的节点通过单链表连接起来,链表的头节点放在哈希数组中。

闭散列的缺陷:1.出现严重哈希冲突时,查找效率降低显著。2.需要严格控制负载因子,需要频繁扩容且空间利用率低。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐