类的多态

1、多态的定义
多态指同一个实体同时具有多种形式。它是面向对象程序设计的一个重要特征。如果一个语言只支持类而不支持多态,只能说明它是基于对象的,而不是面向对象的。C++中的多态性具体体现在运行和编译两个方面。运行时多态是动态多态,其具体引用的对象在运行时才能确定。编译时多态是静态多态,在编译时就可以确定对象使用的形式。同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
c++中有5种方法来实现虚函数,抽象类,重载,覆盖,模版。
2.对象的类型的确定
首先,前边我们已经提到过多态分为动态多态(运行)与静态动态(编译),对象的类型呢,也分为动态类型和静态类型。
这里写图片描述
来看一下下边的例子,就会明白他具体的意思是怎么回事啦
这里写图片描述
3.多态的分类
3.1静态多态(函数重载、泛型编程)
编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误。
下面是一个函数重载的例子:
这里写图片描述
3.2动态多态(虚函数)
动态绑定:在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。
使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。
基类中指定虚函数必须写上virtual关键字,派生类中可以不用写,但最好还是写上,这样看起开更明确。
动态绑定的条件分为以下俩点:
(1)必须是虚函数
(2)通过基类类型的的指针或者引用调用虚函数
看下面的程序:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <stdlib.h>
using namespace std;
class A
{
public:
    A(int a) 
    :_a(a)
    {}
    virtual void FunTest()//虚函数,可以再子类中进行重新定义
    {
        cout << _a << endl;
    }
private:
    int _a;
};
class B:public A
{
public:
    B(int a, int b)
    :A(a)
    ,_b(b)
    {}
    virtual void FunTest()//重新定义
    {
        cout << _b << endl;
    }
private:
    int _b;
};
int main()
{
    A a(2);
    A* pa;//定义基类指针
    B b(3, 4);
    pa = &a;//pa指向基类
    pa->FunTest();
    pa = &b;//pa指向派生类
    pa->FunTest();
    system("pause");
    return 0;
}

结果为:
这里写图片描述
如果不是虚函数,则每次只是调用基类的FunTest函数。
来看下面一道程序:

class CBase
{
public:
    virtual void FunTest1(int _iTest){ cout << "CBase::FunTest1()" << endl; }
    void FunTest2(int _iTest){ cout << "CBase::FunTest2()" << endl; }
    virtual void FunTest3(int _iTest1){ cout << "CBase::FunTest3()" << endl; }
    virtual void FunTest4(int _iTest){ cout << "CBase::FunTest4()" << endl; }
};
class CDerived :public CBase
{
public:
    virtual void FunTest1(int _iTest){ cout << "CDerived::FunTest1()" << endl; }
    virtual void FunTest2(int _iTest){ cout << "CDerived::FunTest2()" << endl; }
    void FunTest3(int _iTest1){ cout << "CDerived::FunTest3()" << endl; }
    virtual void FunTest4(int _iTest1, int _iTest2)
    {
        cout << "CDerived::FunTest4()" << endl;
    }
};
int main()
{
    CBase* pBase = new CDerived;
    pBase->FunTest1(0);
    pBase->FunTest2(0);
    pBase->FunTest3(0);
    pBase->FunTest4(0);
    system("pause");
    return 0;
}

结果为:
这里写图片描述
从此,我们引出了继承体系中同名成员函数之间的关系:
这里写图片描述
3.3纯虚函数
在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。
抽象类使用规则:
<1>抽象类只能作为其他类的基类来使用,不能建立抽象类对象。
<2>不允许从具体类派生出抽象类。
<3>抽象类不能用作函数的参数类型、函数的返回类型或者显式转换的类型。
<4>可以声明指向抽象类的指针或引用,此指针可以指向它的派生类,进而实现多态性。
<5>如果派生类中没有定义纯虚函数的实现,而派生类只是继承基类的纯虚函数,则这个派生类仍然是一个抽象类。如果派生类给出了基类纯虚函数的实现,则该派生类不再是抽象类,而是可以建立对象的具体类。
总结:
1、派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
2、基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
3、只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数。
4、如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加。
5、构造函数不能定义为虚函数,虽然可以将operator=定义为虚函数,但最好不要这么做,使用时容
易混淆
6、不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会
出现未定义的行为。
7、最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构
函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)
8、虚表是所有类对象实例共用的

4、虚表剖析
对于有虚函数的类(即抽象类),编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针,虚表中按照虚函数在类中的声明顺序依次存放了各个虚函数的地址。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
某公司的员工有经理Manager、技术人员Technicist和营销人员SalsePerson,他们的薪金计算方法如下: 经理按月计酬,方法是:基本工资+奖金;技术人员按月计酬,方法是:基本工资;营销人员按月计酬,方法是:基本工资+销售利润*5%。 每类人员都有职工编号、姓名、性别、入职时间、职位、基本工资等数据,其中为入职时间定义Date类,并为该类重载运算符<<,实现入职时间的输入;各类人员使用统一接口getpay()计算各类人员的月薪。其次,设计一个统计并输出该公司每个人员某几个月薪金情况的报表类Report,该类提供add接口向Report类的容器中添加员工信息,并提供print接口用于输出每个员工的职工编号、姓名、性别、入职时间、职位和在设定的月份时间段中该员工的薪酬总额。为了方便实现查找功能,为Report类重载[]运算符的功能,下标值为职位,能根据职位信息查找出所有符合该职位的员工,并重载print接口,输出查找出的员工信息,信息包括职工编号、姓名、性别、入职时间、职位、基本工资。在主函数中对实现的类进行测试,首先,创建各类人员对象,通过Report类的add接口向报表中添加这些人员信息,然后通过Report类的print接口输出报表。其次测试报表的查找功能,输入要查找的员工职位信息,通过Report类的print接口输出查找到的员工基本信息报表。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值