1.虚函数及虚函数表
- 虚函数是通过虚函数表来实现的,虚函数表存的是一个类定义的所有虚函数地址。虚函数表解决了继承覆盖的问题,在有虚函数的实例中,虚函数表被分配在了这个对象的内存中。而且由于编译器保证了虚函数表指针在对象内存的最前面,这样就可以通过对象实例的地址得到虚函数表,通过遍历虚函数表得到虚函数地址。
- 虚函数的主要作用是通过父类的指针和或引用来调用子类的成员函数。不同的类具有不同的虚函数表,同属一个类的不同的实例化对象是共用一张虚函数表的。
- 在C++多继承中,子类继承了几个基类就会有几个虚表。如果类c继承了类a和类b,则c对象有两个虚函数表指针指向两个虚函数表。c中覆盖的虚函数会替换掉两个表中同名的虚函数指针位置。若是普通多重继承,根据继承顺序, c 中多余的虚函数的地址会附加到第一个虚函数表指针指向的虚函数表的后面。
- 虚函数表属于类,类的所有对象共享这个类的虚函数表。虚函数表由编译器在编译时生成,保存在.rdata只读数据段。
2.哪些函数不能是虚函数
1)普通函数
普通函数不属于成员函数,是不能被继承的。普通函数只能被重载,不能被重写,因此声明为虚函数没有意义。因为编译器会在编译时绑定函数。
而多态体现在运行时绑定。通常通过基类指针指向子类对象实现多态。
2)友元函数
友元函数不属于类的成员函数,不能被继承。对于没有继承特性的函数没有虚函数的说法。
3)构造函数
-
从内存的角度看:虚函数对应一个虚函数表,虚函数表是存储在对象的内存空间中的。而调用虚函数使用过调用虚函数指针在虚函数表中找到对应的函数地址来实现的。如果构造函数是虚函数,那么就需要通过虚函数表来实现,但是对象是通过构造函数实例化的,在调用构造函数之前,虚函数表内存空间还没有被创建,无法找到虚表。所以构造函数不能是虚函数。
-
从使用的角度看:虚函数是通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。,但是构造函数是通过创建对象时自动调用的,不可能通过父类的指针或者引用去调用,所以规定构造函数不能是虚函数。
4)内联成员函数
我们需要知道内联函数就是为了在代码中直接展开,减少函数调用花费的代价。也就是说内联函数是在编译时展开的。而虚函数是为了实现多态,是在运行时绑定的。因此显然内联函数和多态的特性相违背。
5)静态成员函数
首先静态成员函数理论是可继承的。但是静态成员函数是编译时确定的,无法动态绑定,不支持多态,因此不能被重写,也就不能被声明为虚函数。