1、用virtual关键字说明的函数
2、虚函数是实现运行时多态性的基础
3、C++中的虚函数是动态绑定的函数
4、虚函数必须是非静态的成员函数,虚函数经过派生之后,就可以实现运行过程中的多态
Ex:
#include <iostream> using namespace std; class Base1 { public: virtual void display() const; //虚函数 }; void Base1::display() const { cout << "Base1::display()" << endl; } class Base2::public Base1 { public: virtual void display() const; }; void Base2::display() const { cout << "Base2::display()" << endl; } class Derived: public Base2 { public: virtual void display() const; }; void Derived::display() const { cout << "Derived::display()" << endl; } void fun(Base1 *ptr) { ptr->display(); } int main() { Base1 base1; Base2 base2; Derived derived; fun(&base1); fun(&base2); fun(&derived); return 0; }
1、一般成员函数可以是虚函数
2、构造函数不能是虚函数
3、析构函数可以是虚函数
1、虚函数的声明
virtual 函数类型 函数名(形参表)
2、虚函数声明只能出现在类定义中的函数原型声明中,而不能在成员函数实现的时候。
3、在派生类中可以对基类中的成员函数进行覆盖
4、虚函数一般不声明为内联函数,因为对虚函数的调用需要动态绑定,而对内联函数的处理是静态的
5、千万不要重写继承而来的非虚函数
1、派生类可以不显式地用virtual声明虚函数,这时系统就会用以下规则来判断派生类的一个函数成员是不是虚函数:
(1)该函数是否与基类的虚函数有相同的名称,参数个数及对应参数类型
(2)该函数是否与基类的虚函数有相同的返回值或者满足类型兼容规则的指针,引用型的返回值。
2、如果从名称、参数及返回值三个方面检查之后,派生类的函数满足上述条件,就会自动确定为虚函数。这时派生类的虚函数便覆盖了基类的虚函数。
3、派生类中的虚函数还会隐藏基类中同名函数的所有其它重载形式。
4、一般习惯于在派生类的函数中也使用virtual关键字以增加程序的可读性。
如果你打算允许其他人通过基类指针调用对象的析构函数(通过delete这样做是正常的)就需要让基类的析构函数成为虚函数,否则执行delete的结果是不确定的。
Ex:如下面的程序,如果不使用虚析构函数,那么只会执行基类的析构函数,而不会执行派生类的析构函数
#include <iostream> using namespace std; class Base { public: ~Base(); //不是虚函数 }; Base::~Base() { cout<< "Base destructor" << endl; } class Derived: public Base{ public: Derived(); ~Derived(); //不是虚函数 private: int *p; }; Derived::Derived() { p=new int(0); } Derived::~Derived() { cout<<"Derived Constructor"<<endl; delete p; } void fun(Base *b) { delete b; //静态绑定只会调用~Base() } int main() { Base *b = new Derived(); fun(b); return 0; } //虚析构函数 #include <iostream> using namespace std; class Base { public: virtual ~Base(); }; Base::~Base() { cout<< "Base destructor" << endl; } class Derived: public Base{ public: Derived(); virtual ~Derived(); private: int *p; };
1、每个多态类有一个虚表(virtual table)
2、虚表中有当前类的各个虚函数的入口地址
3、每个对象有一个指向当前类的虚表的指针(虚指针vptr)
1、构造函数中为对象的虚指针赋值
2、通过多态类型的指针或引用调用成员函数时,通过虚指针找到虚表,进而找到所调用的虚函数的入口地址
3、通过该入口地址调用虚函数
class Base{ public: virtual void f(); virtual void g(); private: int i; }; class Derived:public Base{ public: virtual void f(); //覆盖Base::f() virtual void h(); //新增 private: int j; };