方法在基类中被声明为virtual的后,它在派生类中将自动成为虚方法。此时我们在派生类中将此方法声明不声明为virtual都没关系了,但是最好是声明出来好标记哪些方法是虚的。
析构函数和构造函数要被声明为虚函数吗:
继承体系中,析构函数应该被声明为虚函数。如果不声明为虚函数,一个基类指针指向派生类对象时,delete这个指针时,将只会调用这个指针类型的(基类的)析构函数。如果声明为虚函数后,使用的将是晚期联编,会在程序运行时,判断调用哪个函数。会根据这个指针指向的对象类型来确定,因此会调用派生类的析构函数。
构造函数不用声明为虚函数,因为在构造这个派生类时,会先调用基类构造函数,再调用它自己的。
class Base {
public:
Base() { cout << "base contructor" << endl; };
virtual ~Base() { cout << "base destructor" << endl; };
};
class Derived :public Base {
public:
Derived() { cout << "derived contructor" << endl; };
virtual ~Derived() { cout << "derived destructor" << endl; };
};
int main() {
Base* p = new Derived; //输出base constructor,derived constructor
delete p; //输出derived destructor,base destructor
return 0;
}
按值赋值与按指针或引用赋值的不同:
按值赋值导致只将Derived对象d的Base部分传给bv。所以调用的func函数也是Base的。但是派生类引用或指针在赋值时,可以被转换为基类引用或指针(隐式向上强制转换)。
#include<iostream>
using namespace std;
class Base {
public:
virtual void func() { cout << "base function" << endl; }
};
class Derived :public Base {
public:
virtual void func() { cout << "derived function" << endl; }
};
int main() {
Derived d;
Base *bp=&d;
bp->func(); //输出derived function
Base bv = d;
bv.func(); //输出base function
}
虚函数的工作原理:
虚函数使用的是晚期联编,只有在程序运行时才能确定到底要调用哪个函数。含有虚函数的类会有一个虚函数表,这个表里存放着它所有虚函数的地址。类的每个对象会添加一个指向虚函数表的指针。基类中有虚函数,它会有一张虚函数表。从基类派生出来的派生类,也会有一张虚函数表。它拷贝基类的虚函数表。如果派生类重写了基类中的虚函数,那么新的函数地址会覆盖原先的函数地址。在派生类中新加的虚函数也会加到该表后面。
多继承下的虚函数:
他继承的所有基类中,有多少个带虚函数的基类,派生类就会有多少个虚函数表,他会把他们的虚函数表都拷贝一份。派生类会重写每一个基类的每一个同名函数。派生类中新加入的虚函数,会加入到第一个虚函数表后面。