多态&虚函数

虚函数:在类的成员函数前面加上关键字“virtual”,就称这个成员函数为虚函数。

虚函数重写:在子类中定义了一个与父类完全相同(函数名和返回类型以及参数列表都相同)的虚函数时,称子类的这个函数重写(也称覆盖)了父类的这个虚函数。

多态:一个类继承另一个类时,对一个函数进行重写,则此函数呈多态(多种形态)。

为了指明某个成员函数具有多态性,就用关键字“virtual”来标志其为虚函数。
#include<iostream>
#include<string>
using namespace std;

class Person//父类
{
public:
    virtual void BuyTickets()//虚函数
    {
        cout << "全价票" << endl;
    }
};

class Student :public Person//子类
{
public:
    virtual void BuyTickets()//虚函数 
    {
        cout << "半价票" << endl;
    }
};

void Fun(Person& p)
{
    p.BuyTickets();
}

void Test()
{
    Person p;
    Student s;
    Fun(p);
    Fun(s);
}

上面的函数中void BuyTickets()函数就呈多态
这里写图片描述

这里写图片描述
上图的前提条件式是void BuyTickets()必须构成多态

这里我们要注意判断调用的函数是否构成多态?
1、父类的指针或者引用

class Person
{

public:
     virtual void BuyTickets()
     {
        cout << "全价票" << endl;
    }
};

class Student:public Person
{
public:
    virtual void BuyTickets()
    {
        cout << "半价票" << endl;
    }
protected:
    int _num;
};

//父类的指针或者引用
void Fun(Person* p)
//void Fun(Person& p)
{
    p->BuyTickets();
}

void Test()
{
    Person p;
    Student s;
    Fun(&p);
    Fun(&s);
}

2、虚函数的重写(virtual+函数完全相同(协变例外))
协变是一种例外,函数返回值可以不同但有条件,条件是返回类型必须是构成父子关系的指针或引用
下面就是协变的情况

//返回值类型构成父子关系的指针或者引用
//指针

class Person
{
public:
    virtual Person* BuyTickets()    

        {
        cout << "全价票" << endl;
        return this;
    }
};

class Student :public Person
{
public:
    virtual Student* BuyTickets()
    {
        cout << "半价票" << endl;
        return this;
    }
};


//引用

class Person
{
public:
    virtual Person& BuyTickets()
    {
        cout << "全价票" << endl;
        return *this;
    }
};

class Student :public Person
{
public:
    virtual Student& BuyTickets()
    {
        cout << "半价票" << endl;
        return *this;
    }
};

这种协变的情况也是能构成多态的

注:BuyTickets()在父类中声明为virtual,该虚函数的性质自动的继承给子类,所以Student子类中的virtual可以省略;
如果要在类外定义虚函数,只能在声明函数时加virtual,类外定义函数时不能加virtual;


注意:1、最好把基类的析构函数声明为虚函数

Why?

void func(Base* p)
{
//...
delete p;
}

p是传递过来的一个对象指针,它或者指向基类对象,或者子类对象。在执行delete p时,要调用析构函数,但不知道是指向基类的析构还是子类的析构,将析构函数声明为虚函数就可以解决这个问题(基类的析构函数与子类的析构函数构成多态)。

注意:2、 构造函数不能是虚函数并且构造函数里也不能调用虚函数

因为构造时对象是不完整的,对象还是一片未定型的空间,可能会发生未定义的行为,只有在构造完成后,对象才能成为一个完整的类的实例。

注意:3、静态成员函数不能定义为虚函数,因为静态成员函数不受限于某个对象

纯虚函数

class Person
{
virtual void Display()=0;//纯虚函数
protected:
string _name;
}

class Student:class Person
{
//...
}

在成员函数的形参后面写上=0,则成员函数为虚函数。包括纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。

其实纯虚函数就是在基类中为子类保留的一个位置,以便以后子类用自己实在的函数定义来覆盖它。如果在基类中没有保留位置,则就不能覆盖了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值