首页 > 编程笔记

C++纯虚函数和抽象类

有时候在基类中声明函数并不是基类本身的需要,而是考虑到派生类的需求,在基类中声明一个函数,函数的具体实现由派生类根据本类的需求定义。

例如,动物都有叫声,但不同的动物叫声不同,因此基类(动物类)并不需要实现描述动物叫声的函数,只需要声明即可,函数的具体实现在各派生类中完成。

在基类中,这样的函数可以声明为纯虚函数。

C++纯虚函数

纯虚函数也通过virtual 关键字声明,但是纯虚函数没有函数体。纯虚函数在声明时,需要在后面加上“=0”,格式如下所示:

virtual 函数返回值类型 函数名(参数列表) =  0;

上述格式中,纯虚函数后面“=0”并不是函数的返回值为 0,它只是告诉编译器这是一个纯虚函数,在派生类中会完成具体的实现。

纯虚函数的作用是在基类中为派生类保留一个接口,方便派生类根据需要完成定义,实现多态。

派生类都应该实现基类的纯虚函数,如果派生类没有实现基类的纯虚函数,则该函数在派生类中仍然是纯虚函数。

C++抽象类

如果一个类中包含纯虚函数,这样的类称为抽象类。抽象类的作用主要是通过它为一个类群建立一个公共接口(纯虚函数),使它们能够更有效地发挥多态性。抽象类声明了公共接口,而接口的完整实现由派生类定义。

抽象类只能作为基类派生新类,不能创建抽象类的对象,但可以定义抽象类的指针或引用,通过指针或引用操作派生类对象。

抽象类可以有多个纯虚函数,如果派生类需要实例化对象,则在派生类中需要全部实现基类的纯虚函数。如果派生类没有全部实现基类的纯虚函数,未实现的纯虚函数在派生类中仍然是纯虚函数,则派生类也是抽象类。

【示例1】下面通过案例演示纯虚函数和抽象类的应用,C++ 代码如下:
#include<iostream>
using namespace std;
class Animal      //动物类Animal
{
public:
    virtual void speak()=0;    //纯虚函数speak()
    virtual void eat()=0;   //纯虚函数eat()
    virtual ~Animal();     //虚析构函数
};
Animal::~Animal()
{
    cout<<"调用Animal析构函数"<<endl;
}
class Cat:public Animal    //猫类Cat,公有继承Animal类
{
public:
    void speak();      //声明speak()函数
    void eat();     //声明eat()函数
    ~Cat();      //声明析构函数
};
void Cat::speak()     //实现speak()函数
{
    cout<<"小猫喵喵叫"<<endl;
}
void Cat::eat()     //实现eat()函数
{
    cout<<"小猫吃鱼"<<endl;
}
Cat::~Cat()      //实现析构函数
{
    cout<<"调用Cat析构函数"<<endl;
}
class Rabbit:public Animal    //兔子类Rabbit,公有继承Animal类
{
public:
    void speak();      //声明speak()函数
    void eat();     //声明eat()函数
    ~Rabbit();      //声明析构函数
};
void Rabbit::speak()    //实现speak()函数
{
    cout<<"小兔子咕咕叫"<<endl;
}
void Rabbit::eat()     //实现eat()函数
{
    cout<<"小兔子吃白菜"<<endl;
}
Rabbit::~Rabbit()     //实现析构函数
{
    cout<<"调用Rabbit析构函数"<<endl;
}
int main()
{
    Animal* pC=new Cat;    //定义基类指针pC指向Cat类对象
    pC->speak();      //通过pC指针调用Cat类的speak()函数
    pC->eat();      //通过pC指针调用Cat类的eat()函数
    delete pC;      //释放pC指针指向的空间
    Animal* pR=new Rabbit;    //定义基类指针pR指向Rabbit类对象
    pR->speak();      //通过pR指针调用Rabbit类的speak()函数
    pR->eat();      //通过pR指针调用Rabbit类的eat()函数
    delete pR;      //释放pR指针指向的空间
    return 0;
}
运行结果:

小猫喵喵叫
小猫吃鱼
调用Cat析构函数
调用Animal析构函数
小兔子咕咕叫
小兔子吃白菜
调用Rabbit析构函数
调用Animal析构函数

示例分析:
由运行结果可知,Cat 类和 Rabbit 类对象创建成功,并且通过基类指针 pC 和 pR 成功调用了各派生类的函数,实现了多态。在释放指针指向的空间时,先调用了派生类的析构函数,后调用了基类析构函数。

上述示例中 Animal 类是抽象类,如果创建 Animal 类对象,编译器会报错,例如在 main() 函数中添加如下代码:

Animal animal;

再次运行程序,编译器会报错,如下所示:

“Animal”:不能实例化抽象类
不允许使用抽象类类型“Animal”的对象:

如果 Animal 类的某个派生类没有全部实现纯虚函数,则派生类也是抽象类,不能创建该派生类的对象。

优秀文章