多态的底层实现逻辑

编译器找对应虚函数的步骤:

this指针->vptr指针->vtable表(虚函数表)->对应的虚函数(通过指针偏移,vptr就是虚函数表的起始指针,函数指针在虚函数表占的位置也是固定的)。

其实之前一直没明白为什么重定义不能实现多态?这两个例子就可以很好区分:

重定义:

代码语言:javascript

AI代码解释

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
using namespace std;

class Animal {
public:
    void speak() {
        std::cout << "Animal speaks" << std::endl;
    }
};

class Dog : public Animal {
public:
    void speak(){
        std::cout << "Dog barks" << std::endl;
    }
};

class Cat : public Animal {
public:
    void speak() {
        std::cout << "Cat meows" << std::endl;
    }
};

int main() {
    Animal* a1 = new Dog();
    Animal* a2 = new Cat();

    a1->speak(); 
    a2->speak();  

    delete a1;
    delete a2;
}

在上面这个例子中,a1,a2都是Animal类型,他们在不同的作用域中,所以不会造成冲突。在调用speak函数的时候,直接去基类作用域Animal中寻找,都是找到的是基类的speak函数。(这和编译器寻找虚函数完全不同)

重写/覆盖

在Dog和Cat类中,都有一张虚函数表,他们继承父类Anima的虚函数表,因为在Cat和Dog中对soeak函数都进行了重写,所以他们自己的虚函数表的指针就发生了变化(在虚函数表中,对父类的speak函数进行覆盖)。这样在调用对应的虚函数的时候,就可以分别调用不同的虚函数了。调用的过程也是上面这样:his指针->vptr指针->vtable表(虚函数表)->对应的虚函数


为什么只有非静态成员函数才可以是虚拟的?

一个非静态成员函数函数在前面加上virtual就变成了虚函数。

因为虚函数是在虚函数表中的,虚函数表要靠vptr才能找到,vptr要这个对象的this指针才能找到。静态成员函数是不需要靠


虚函数的分类有:纯虚函数和普通虚函数。

有纯虚函数的类不能进行实例化,必须在子类重写以后才能实例化。

1.什么情况下会有虚函数表?

当一个类有虚函数的时候,就会有虚函数表。

2.实例化出来的对象是怎么去调用虚函数的?

对象会有一个指向虚函数表的虚指针vptr这个指针在64位平台是8字节,在32位平台是4字节),vptr是虚函数表的起始指针,通过指针的偏移就可以找到对应的虚函数地址,然后就可以进行函数调用。


函数重载,重定义,重写/覆盖:

函数重载(静态多态

在同一个作用域类,函数名相同,参数不同的函数构成函数重载。仅仅是返回值不同,不能构成函数重载。

问题:为什么编译器可以根据参数的不同,构造出不同的函数,从而形成函数重载,但是仅仅返回值不同,不能构成函数重载?

为了定位一个具体的函数,编程语言中引入了函数签名。

函数签名

1.函数名称。

2.参数列表:参数的个数,类型,顺序不同,都可以视作为参数列表的不同。

在C++语言中,函数签名没有包含返回值(但是有的编程语言中把返回值也作为函数签名的一部分),所以如果一个函数的名称相同,参数列表相同。只有返回值不同,他们的函数签名是一样的。编译器就不能区分他们了。

所以函数名相同,参数的顺序和个数和类型不同,可以区分出不同的函数,这些函数叫函数虫子啊。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐