【C++】虚函数表(vtable),函数重载,函数签名,
代码语言:javascriptAI代码解释public:public:public:delete a1;delete a2;在上面这个例子中,a1,a2都是Animal类型,他们在不同的作用域中,所以不会造成冲突。在调用speak函数的时候,直接去基类作用域Animal中寻找,都是找到的是基类的speak函数。(这和编译器寻找虚函数完全不同)静态多态。
多态的底层实现逻辑
编译器找对应虚函数的步骤:
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++语言中,函数签名没有包含返回值(但是有的编程语言中把返回值也作为函数签名的一部分),所以如果一个函数的名称相同,参数列表相同。只有返回值不同,他们的函数签名是一样的。编译器就不能区分他们了。
所以函数名相同,参数的顺序和个数和类型不同,可以区分出不同的函数,这些函数叫函数虫子啊。
更多推荐



所有评论(0)