C++语言中的纯虚函数与抽象类

上一节较为详细的讨论了C++语言中基类被派生类继承过程中的内存模型,尤其较为详细的分析了虚函数及其虚表、虚表指针在内存中是如何分布,如何存储的,这对于理解C++语言中的“动态绑定”是极有帮助的。

正如之前两篇文章所讨论的,C++语言中虚函数的“动态绑定”能为多态的实现带来极大的便利——“动态绑定”机制是在程序运行时根据指针所指向对象的类型(而不是指针本身类型)决定被调用的成员函数,因此在C++语言程序开发中,使用基类指针就可以调用其所有派生类的成员函数。可见,C++语言中的虚函数其实就是为了提升程序员开发的便利设计的。事实上,为了充分利用这种设计带来的便利性,C++语言中还有着“纯虚函数”的概念。

纯虚函数与抽象类

所谓“纯虚函数”,其实就是没有具体实现的虚函数,通常定义在基类中提供类似于“接口”的功能。因为没有具体实现,也即该函数没有具体功能,拥有纯虚函数的基类通常被称作“抽象类”,所以抽象类无法实例化对象,一般只能作为基类被其他派生类继承使用。继承了抽象基类的派生类必须重写所有的纯虚函数,也即为类提供具体的功能,才能实例化对象使用。

在C++语言中,只要某个类有纯虚函数,或者继承了抽象类而没有重写所有纯虚函数,那么该类就是“抽象类”,无法实例化对象。

在C++语言中,在类中定义纯虚函数是非常简单的,只需在函数后使用=0就可以了,例如下面这段示例代码:

class Base {
    public:
        void f1(){...}      // 非虚函数
        virtual void f2(){...}  // 虚函数,但是不纯
        virtual void f3() = 0;  // 纯虚函数
};
Base b; // 非法,纯虚函数 f3() 没有具体实现(功能)

上面的 Base 就是一个抽象类(因为它有一个纯虚函数 f3()),所以无法直接使用 Base 实例化对象。正如前文所说,抽象类通常是作为基类使用的,例如下面这段C++语言代码示例:

class Derived : public Base {
        // 没有 f1(),没问题
        // 没有 f2(),没问题
        void f3(){...} // 重写 f3()
};
Derived d;  // 合法, Derived::f3 重写了 Base::f3

Derived 类继承了抽象基类 Base,并且重写了纯虚函数 Base::f3(),因此 Derived 类可以实例化对象 d 使用。注意到派生类 Derived 重写 f3() 时并未显式的使用 virtual 关键字,这是没有问题的,只要保证函数名、参数、返回值都与基类 Base 中的纯虚函数一致,编译器会自动将其识别为虚函数的。

因为派生类 Derived 重写了基类 Base 中的纯虚函数,因此它不再是抽象类,其他继承 Derived 的派生类也不再是抽象类,除非这些派生类定义了自己的纯虚函数。

纯虚函数也可以有功能

基类中的纯虚函数也可以像其他普通函数那样提供一些通用的功能,例如:

void Base::f3(){
    cout<< "ok\n";
}

但是要注意,即使纯虚函数 f3() 有自己的功能,基类 Base 仍然是抽象类,还是不能够实例化对象,它的派生类必须重写 f3(),否则该派生类也是抽象类,只不过派生类在重写 f3() 时,可以调用 Base::f3(),例如下面这段C++语言代码示例:

void Derived::f3() {
    ...
    Base::f3();
}

纯虚函数非常有用

纯虚函数可以提供抽象类的功能,抽象类则通常提供统一接口的功能,强迫继承它的派生类各自实现符合自己特点的具体功能,并且一定要有这样的功能,这对于设计和架构非常有用。例如,设计师设计了一个汉语翻译程序,他定义了一个基类,该基类中有两个纯虚函数:

class Base {
    public:
        virtual string translate_to(string raw) = 0;
        virtual string translate_from(string raw) = 0;
        void help() {
            ...
        };
};

其中 translate_to() 函数负责将其他外语翻译为汉语,translate_from() 函数负责将汉语翻译为其他外语,因为暂时还不知道具体要实现哪种外语与汉语的翻译,因此这两个函数都是纯虚函数。在接下来的C++语言程序开发中,各种子外语都需要继承 Base,也即必须提供各自的翻译实现,设计师的基本功能要求就成了必须完成的任务。从上面的这个例子可以看出,借助于C++语言的纯虚函数机制,设计师无需再提供详细的功能实现,仅需设计出基本功能要求就可以了。至于非虚函数 Base::help() 函数则可以提供通用的功能,比如输出此程序的帮助信息。

小结

稍稍思考下,应该能够发现C++语言中的纯虚函数其实提供了一种“强制”功能——纯虚函数必须被具体实现,才能够实例化对象使用,从文章末尾的例子能够看出这非常有用。这么看来,C++语言中的纯虚函数倒有些类似于“必须实现的接口”了,的确如此,实际上在实际的C++语言程序开发中,如果某个抽象类没有成员变量,它的所有函数均为纯虚函数,那么它就是一组函数接口。

阅读更多:   C++虚函数
添加新评论

icon_redface.gificon_idea.gificon_cool.gif2016kuk.gificon_mrgreen.gif2016shuai.gif2016tp.gif2016db.gif2016ch.gificon_razz.gif2016zj.gificon_sad.gificon_cry.gif2016zhh.gificon_question.gif2016jk.gif2016bs.gificon_lol.gif2016qiao.gificon_surprised.gif2016fendou.gif2016ll.gif