是编译器分配一段只读数据区。
编译器找对应虚函数的步骤:
this指针->vptr指针->vtable表(虚函数表)->对应的虚函数(通过指针偏移,vptr就是虚函数表的起始指针,函数指针在虚函数表占的位置也是固定的)。
其实之前一直没明白为什么重定义不能实现多态?这两个例子就可以很好区分:
#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指针才能找到。静态成员函数是不需要靠
有纯虚函数的类不能进行实例化,必须在子类重写以后才能实例化。
当一个类有虚函数的时候,就会有虚函数表。
对象会有一个指向虚函数表的虚指针vptr(这个指针在64位平台是8字节,在32位平台是4字节),vptr是虚函数表的起始指针,通过指针的偏移就可以找到对应的虚函数地址,然后就可以进行函数调用。
在同一个作用域类,函数名相同,参数不同的函数构成函数重载。仅仅是返回值不同,不能构成函数重载。
问题:为什么编译器可以根据参数的不同,构造出不同的函数,从而形成函数重载,但是仅仅返回值不同,不能构成函数重载?
为了定位一个具体的函数,编程语言中引入了函数签名。
1.函数名称。
2.参数列表:参数的个数,类型,顺序不同,都可以视作为参数列表的不同。
在C++语言中,函数签名没有包含返回值(但是有的编程语言中把返回值也作为函数签名的一部分),所以如果一个函数的名称相同,参数列表相同。只有返回值不同,他们的函数签名是一样的。编译器就不能区分他们了。
所以函数名相同,参数的顺序和个数和类型不同,可以区分出不同的函数,这些函数叫函数虫子啊。
重定义在上面已经讲过了。