纯虚函数的格式:
virtual <类型> <函数名> (参数表) =0;
而我们的纯虚函数经常被用来构建抽象基类。
抽象基类:当我们仅想对基类进行向上类型转换,使用它的接口,而不希望用户实际地创建一个基类的对象,那么我们就在基类中加入至少一个纯虚函数(pure virtual function),来使基类称为抽象(abstract)类。
当继承一个抽象类时,必须实现所有的纯虚函数,否则继承出的类也将是一个抽象类。
实例:
//Pure abstract base classes
#include <iostream>
using namespace std;
//enum note {middleC,Csharp,Cflat};
class Instrument
{
public:
//Pure virtual functions:
// virtual void play(note) const =0;
virtual void play() const =0;
virtual const char* what() const =0;
//Assume this will modify the object:
virtual void adjust(int) =0;
};
//Rest of the file is the same...
class Wind: public Instrument
{
public:
void play() const
{
cout<< "Wind::play" <<endl;
}
const char* what() const
{
return "Wind";
}
void adjust(int)
{
cout<<"Wind.adjust()"<<endl;
}
};
class Stringed: public Instrument
{
public:
void play() const
{
cout<<"Stringed::play"<<endl;
}
const char* what() const
{
return "Stringed";
}
void adjust(int){}
};
class Brass: public Wind
{
public:
void play() const
{
cout<<"Brass::play"<<endl;
}
const char* what() const
{
return "Brass";
}
};
//Identical function from before:
void tune(Instrument& i)
{
//...
i.play();
}
//New function:
void f(Instrument& i)
{
i.adjust(1);
}
int main()
{
Wind flute;
Stringed violin;
Brass flugelhorn;
tune(flute);
tune(violin);
tune(flugelhorn);
f(flugelhorn);
}
以上代码中,Instrument的目的是对所有从它派生出来的类创建公共接口。
既然是接口,那么我们实际上并不需要实例化Instrument对象,那么写成纯虚函数的目的也是让编译器禁止实例化。
纯虚函数告诉编译器在VTABLE中为函数保留一个位置,但在这个特定位置中不放地址,那么VTABLE就是不完全的,所以编译器就在实例化时发出一个出错信息。
这里需要注意,纯虚函数禁止对抽象类的函数以传值方式调用。这是防止对象切片(object slicing)的一种方法。通过抽象类,可以保证在向上类型转换器件总是使用指针或引用。
以上代码的执行结果:
Wind::play
Stringed::play
Brass::play
Wind.adjust()