一、虚函数
被virtual关键字修饰的类成员函数就是虚函数。虚函数的作用就是实现运行时的多态性,将接口与实现分离。简单理解就是相同函数有着不同的实现,但因个体差异而采用不同的策略。
基类中提供虚函数的实现,为派生类提供默认的函数实现。派生类可以重写基类的虚函数以实现派生类的特殊化。如下:
class Base{
public:
virtual void foo() { cout<<"Base::foo() is called"<<endl; }
};
class Derived : public Base {
public:
void foo() { cout<<"Derived::foo() is called"<<endl; }
};
int main(void)
{
Base *b = new Derived();
b->foo(); // b虽然是类型Base的指针 但是实际上指向的是Derived类 所以调用的函数foo是Derived类的
return 0;
}
二、纯虚函数
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加"=0"。
包含纯虚函数的类被称为“抽象类”,抽象类不能实例化成对象,但是可以定义抽象类的引用和指针。此外,实现了该纯虚函数的派生类可以实例化成对象。
class A
{
public:
virtual void out1(string s)=0;
virtual void out2(string s)
{
cout<<"A(out2):"<<s<<endl;
}
};
三、普通函数
普通函数是静态编译的,没有运行时多态,只会根据指针或引用的“字面值”类对象,调用自己的普通函数。
普通函数是基类为派生类提供的“强制实现”。
因此,在继承关系中,派生类不应该重写基类的普通函数,因为函数的调用只与类对象的字面值有关。下面是搬来的一段代码:
#include <iostream>
using namespace std;class A
{
public:
virtual void out1()=0; ///由子类实现
virtual ~A(){};
virtual void out2() ///默认实现
{
cout<<"A(out2)"<<endl;
}
void out3() ///强制实现
{
cout<<"A(out3)"<<endl;
}
};class B:public A
{
public:
virtual ~B(){};
void out1()
{
cout<<"B(out1)"<<endl;
}
void out2()
{
cout<<"B(out2)"<<endl;
}
void out3()
{
cout<<"B(out3)"<<endl;
}
};int main()
{
A *ab=new B;
ab->out1();
ab->out2();
ab->out3();
cout<<"************************"<<endl;
B *bb=new B;
bb->out1();
bb->out2();
bb->out3();delete ab;
delete bb;
return 0;
}
执行结果:
B<out1>
B<out2>
A<out3>
*****************************************
B<out1>
B<out2>
B<out3>
四、虚函数与构造函数和析构函数
1) 当存在类继承并且析构函数中有必须要进行的操作时(如需要释放某些资源,或执行特定的函数)析构函数需要是虚函数,否则若使用父类指针指向子类对象,在delete时只会调用父类的析构函数,而不能调用子类的析构函数,从而造成内存泄露或达不到预期结果;
2) 构造函数不能为虚函数:构造函数在进行调用时还不存在父类和子类的概念,父类只会调用父类的构造函数,子类调用子类的,因此不存在动态绑定的概念;但是构造函数中可以调用虚函数,不过并没有动态果,只会调用本类中的对应函数;
3) 静态成员函数不能为虚函数:静态成员函数是以类为单位的函数,与具体对象无关,虚函数是与对象动态绑定的。
这篇博客介绍的比较详细,我就不搬了……
1> C++ 虚函数表解析
2> C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别
3> C++里神奇的虚函数
4> 虚函数实现机制、构造函数、析构函数能否为虚函数,与纯虚函数