C++基础-虚函数/纯虚函数/普通函数

序言

本文主要内容:虚函数 + 纯虚函数 + 普通函数 + 虚函数纯虚函数的区别


1. 虚函数

  • 什么是虚函数

    • 被virtual关键字修饰的成员函数,就是虚函数
  • 为什么要使用虚函数

    • 引入虚函数主要为了实现重写多态

    • 虚函数主要作用是为了实现“动态多态性”,父类提供虚函数的实现,为子类提供默认的函数实现。子类可以重写父类的虚函数实现子类的特殊化

  • 说明

    • 对于虚函数来说,父类和子类都有各自的版本,由多态方式调用的时候动态绑定。
    • 虚函数必须实现,否则在链接时将会报错


2. 纯虚函数

  • 什么是纯虚函数

    • virtual returnType function() = 0;
  • 为什么要使用纯虚函数

    • 也是为了实现“动态多态性
    • 在很多情况下,基类本身生成对象是不合情理的,动物基类可以派生出老虎、孔雀等,但是动物本身的对象却没有实际意义,所以引入了纯虚函数
  • 什么情况下使用纯虚函数

    • 在基类中抽象出一个方法,且该基类只能被继承不能被实例化
    • 这个方法必须在派生类中被实现。

    满足以上两点,可考虑声明为纯虚函数。


3. 普通函数

  • 类的普通成员函数。

  • 父类为子类提供了普通函数的强制实现

  • 普通函数是静态编译的,可类内重载但没有运行时多态,只会根据指针引用的“字面值”类对象,调用自己的普通函数。


4. 虚函数和纯虚函数举例


  • [1] 普通函数举例(静态联编)
/* 例1 */
class A
{
    public:
        void funPrint()
        {
            cout<<"function in class A"<<endl;
        }
};

class B:public A
{
    public:
        void funPrint()      //重定义
        {
            cout<<"function in class B"<<endl;
        }
};

int main()
{
    A *p;      //基类指针
    A a;       //基类对象
    B b;       //派生类对象

    p = &a;
    p->funPrint();

    p = &b;
    p->funPrint();

    b.funPrint();
    b.A::funPrint();

   return 0;
}

//输出结果:

    function in class A
    function in class A
    function in class B
    function in class A

//原因:1)静态联编:编译器在编译时就确定好了函数调用
    (2)只是普通函数“重定义”,不管引用的实例是哪个类的,调用的时候系统会调用 “左值” 那个对象所属类的方法

    >>> 所以基类A指针p最终调用类A的funPrint()函数,输出两个function in class A
    >>> 类B实例调用类B的funPrint()函数,类B加上作用域操作符A::调用类A的funPrint()函数


/* 例2 */
#include <iostream>
using namespace std;

class A
{
    public:
        virtual void fun1() = 0;             //纯虚函数
        virtual ~A(){};
        virtual void fun2()                  //虚函数
        {
            cout<<"fun1 in class A"<<endl;
        }
        void fun3()                          //普通成员函数
        {
            cout<<"fun3 in class A"<<endl;
        }
};

class B:public A
{
    public:
        virtual ~B(){};
    void fun1()                              //纯虚函数实现
    {
        cout<<"fun1 in class B"<<endl;
    }
    void fun2()                              //虚函数重写(覆盖)
    {
        cout<<"fun2 in class B"<<endl;
    }
    void fun3()                              //普通函数重定义
    {
        cout<<"fun3 in class B"<<endl;
    }
};

int main()
{
    A *a = new B;     //抽象类定义指针
    a->fun1();
    a->fun2();
    a->fun3();

    cout<<"*******************"<<endl;

    B b;               //派生类实例化
    b.fun1();
    b.fun2();
    b.fun3();

    delete a;         //释放 new 的对象
    return 0;
}


//输出结果:

    fun1 in class B           //纯虚函数在基类中声明在子类中实现
    fun2 in class B           //动态联编:多态。虚函数在子类中重写,运行时动态绑定
    fun3 in class A           //静态联编:编译时就确定好了调用哪个函数。调用左值所属类的方法
    *******************
    fun1 in class B
    fun2 in class B           //动态联编:多态。虚函数在子类中重写,运行时动态绑定
    fun3 in class B           //静态联编:调用左值所属类的方法


  • [2] 虚函数举例(动态联编)
class A
{
    public:
        virtual void funPrint()
        {
            cout<<"function in class A"<<endl;
        }
};

class B:public A
{
    public:
        void funPrint()    //或virtual void funPrint(),重写
        {
            cout<<"function in class B"<<endl;
        }
};

int main()
{
    A *p;      //基类的指针
    A a;
    B b;

    p = &a;
    p->funPrint();

    p = &b;
    p->funPrint();

    return 0;
}


//输出结果:

    function in class A
    function in class B

//原因:1)动态联编:根据实例的不同动态决定调用哪个函数
    (2)虚函数的唯一目的就是为了实现多态(动态多态性),编译时不确定调用哪个函数,而是动态的决定要调用哪个函数,即“运行时多态”

    >>> 所以基类A指针p指向类A的实例a就调用类A的funPrint()函数,
    >>> 指向类B的实例b就调用类B的funPrint()函数


  • [3] 纯虚函数举例(抽象类)

    纯虚函数通常存在于抽象类中,在基类中只是声明一个函数但不去实现它,让派生类去实现

class Vehicle
{
    public:
        virtual void PrintTyre() = 0; //纯虚函数
};

class Car:public Vehicle
{
    public:
        virtual void PrintTyre()
        {
            cout<<"Car tyre four"<<endl;
        }
};

class Bike:public Vehicle
{
    public:
        virtual void PrintTyre()
        {
            cout<<"Bike tyre two"<<endl;
        }
};

int main()
{
   Car c;
   Bike b;
   b.PrintTyre();
   c.PrintTyre();
   return 0;
}

//输出结果:

    Car tyre four
    Bike tyre two

//原因:1)定义vehicle类,但是具体每种交通工具有多少个轮子是不一定的,就定义为纯虚函数、留待派生类中具体实现


5. 虚函数和纯虚函数的联系与区别

  • 联系

    • 虚函数和纯虚函数可以定义在同一个类中

    • 虚函数和纯虚函数都可以在子类中被重写,以多态的形式调用

    • 虚函数和纯虚函数通常存在于抽象基类中,被子类继承,目的是提供一个统一的接口

    • 虚函数和纯虚函数的定义中不能有static标识符,因为static要求编译时即绑定,而虚函数和纯虚函数运行时才绑定(动态多态性)

    • 实现了纯虚函数的子类,该纯虚函数在子类中就变成了虚函数,子类的子类即孙子类可以覆盖(重写)该虚函数,由多态方式调用的时候动态绑定

  • 区别

    • 定义

      • 虚函数virtual returnType function();

      • 纯虚函数virtual returnType function() = 0;

    • 抽象类

      • 含有纯虚函数的类称为抽象类,而只含有虚函数的类不能称为抽象类

      • 抽象类不能实例化,但可以定义抽象类的指针引用(见例2)

    • 使用

      • 虚函数可以直接使用,也可以子类重写后以多态形式调用

      • 虚函数在基类中只有声明没有定义,是对子类的约束,是“接口继承”,必须在子类中实现该函数才可以使用



Acknowledgements:
http://www.cnblogs.com/xudong-bupt/p/3570304.html
http://www.cnblogs.com/fzhe/archive/2013/01/02/2842513.html
http://blog.chinaunix.net/uid-26851094-id-3327323.html
http://www.cnblogs.com/crazyacking/p/5265634.html(推荐)
http://www.cnblogs.com/bluestorm/archive/2012/08/29/2662350.html

2017.09.28

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值