多态的相关知识

1.多态:多种形式或形态的情形。
2.多态的分类:<1>.静态多态:发生在编译阶段->函数重载、泛型编程。
<2>.动态多态:程序运行时。动态多态实现的两个条件:a.调用函数必须是虚函数**(一定要在派生类中对基类的虚函数进行重写)**b.通过基类的指针或引用来调用虚函数。
静态多态:(先就函数重载来举例说明)如下代码:

int Add(int a, int b)
{
    return a + b;
}
double Add(double a, double b)
{
    return a + b;
}
int main()
{
    cout << Add(1, 2) << endl;
    cout << Add(1.25, 3.14) << endl;
    system("pause");
    return 0;
}

具体的结果显示如下:
这里写图片描述
动态多态:
使用vistual关键字修饰类的成员函数时,指出该函数为虚函数,派生类重新实现一定要在派生类中对基类的虚函数进行重写。
举例说明如下:

class Base
{
public:
    virtual void test()//成员函数为虚函数
    {
        cout << "Base::test()" << endl;
    }
};
class Derive :public Base//采用公有继承
{
public:
    virtual void test()//在Derive中要对基类的虚函数进行重写。(返回值类型,函数名,参数列表都必须一样)
    {
        cout << "Derive::test()" << endl;
    }
};
int main()
{
    Base a;
    a.test(); 
    Derive b;
    b.test();
    system("pause");
    return 0;
}

运行结果如下:这里写图片描述
简单介绍协变:基类与派生类中函数类型改变。父类的虚函数返回类型是父类的引用或指针,子类的虚函数返回类型是子类的引用或指针,但是还构成重写。

动态多态的第二种形式:通过基类的指针或引用来调用虚函数。具体的例子如下:

class Base
{
public:
    virtual void test()//成员函数为虚函数
    {
        cout << "Base::test()" << endl;
    }
};
class Derive :public Base
{
public:
    virtual void test()
    {
        cout << "Derive::test()" << endl;
    }
};
void Funtest(Base &a)
{
    a.test();
}
int main()
{
    Base a;
    Funtest(a);
    Derive b;
    Funtest(b);
    system("pause");
    return 0;
}

通过基类的指针调用虚函数:具体例子如下:

class Base
{
public:
    virtual void test()//成员函数为虚函数
    {
        cout << "Base::test()" << endl;
    }
};
class Derive :public Base
{
public:
    virtual void test()
    {
        cout << "Derive::test()" << endl;
    }
};
typedef void(*Func)();

int main()
{
    Base a;
    (*((Func*)(*(int *)&a)))();
    system("pause");
    return 0;
}

上面两个例子满足了动态绑定的两个条件,其中还涉及到了继承的赋值兼容规则,我们可以看出,经过编译后,系统对引用所指向的对象进行类型剖析,进行了相应的调用。
简单介绍一下纯虚函数:
在成员函数的形参列表后面写上=0,则成员函数称为纯虚函数,包含纯虚函数的类叫做抽象类(也叫做接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。举例如下:

class Base
{
public:
    virtual void test() = 0;//成员函数为纯虚函数
    int _a;
};
class Derive :public Base
{
public:
    virtual void test()
    {
        cout << "Derive::test()" << endl;
    }
    virtual void test1()
    {
        cout << "Derive::test1()" << endl;
    }
    void test2()
    {
        cout << "Derive::test2()" << endl;
    }
    int _b;
};

抽象类:一个类中至少有一个纯虚函数,就称该类为抽象类。
(1)、抽象类中含有没有任何功能的纯虚函数,所以抽象类只能当做其他类的基类进行应用,不能实例化出对象。
(2)、抽象类不能做函数的参数类型和返回类型。
(3)、可以声明指向抽象类的指针或引用,该指针可以指向它的派生类形成多态。
(4)、纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。

继承体系同名成员函数的关系(即函数重载,重写与重定义的关系)
函数重载:函数重载发生在同一作用域,函数名相同,参数列表不同,返回值可以不同。
重写(覆盖):不在同一作用域(分别在基类和派生类),函数名相同,参数相同,返回值相同(协边例外)。基类函数必须有virtual关键字,访问修饰符可以不同。
重定义(隐藏):在不同作用域中(分别在基类和派生类),函数名相同,在基类和派生类中只要不构成重写就是重定义。

剖析系统对对象的类型解析
下面我们来剖析系统对对象的类型解析。先来剖析单继承对象模型。
具体代码为:

class Base
{
public:
    virtual void test()//成员函数为虚函数
    {
        cout << "Base::test()" << endl;
    }
    int _a;
};
class Derive :public Base
{
public:
    virtual void test()
    {
        cout << "Derive::test()" << endl;
    }
    int _b;
};
int main()
{
    Base a;
    a._a = 1;
    Derive b;
    b._b = 2;
    b._a = 3;
    system("pause");
    return 0;
}

下面通过调试分析其内部内存:如下图所示
这里写图片描述

具体的如图所示:基类的对象模型为:
这里写图片描述
分析派生类的对象模型,若是给派生类内多加一个重写的虚函数和一个普通函数,模型会是怎样呢?
代码如下:

class Base
{
public:
    virtual void test()//成员函数为虚函数
    {
        cout << "Base::test()" << endl;
    }
    int _a;
};
class Derive :public Base
{
public:
    virtual void test()
    {
        cout << "Derive::test()" << endl;
    }
    virtual void test1()
    {
        cout << "Derive::test1()" << endl;
    }
    void test2()
    {
        cout << "Derive::test2()" << endl;
    }
    int _b;
};
int main()
{
    cout << sizeof(Base) << endl;
    cout << sizeof(Derive) << endl;
    Base a;
    a._a = 1;
    Derive b;
    b._b = 2;
    b._a = 3;
    system("pause");
    return 0;
}

结果显示如图:
这里写图片描述
这里写图片描述
进入派生类的虚表中看看:
这里写图片描述可以看出派生类的虚表中只有虚函数。
派生类中的虚表构造为:
(1)、基类中的虚表拷贝一份。
(2)、检测派生类中是否对基类中虚函数进行重写->用派生类中重写的虚函数来替换相同偏移量位置的基类虚函数
(3)、在虚表之后添加派生类自己的虚函数
说明普通函数并没有虚表,且派生类与基类不能使用同一张虚表,有几个类就有几张虚表。

多继承:多继承的相关知识在上篇博客中已经有所介绍,下面来看看多继承中虚表的形式
在多继承中,派生类继承几个基类就有几个虚表指针几个虚表,按继承的顺序排序(先继承的基类的虚表在上面,后继承在下面),然后把派生类自己新增加的虚函数加在第一张虚表的后面。具体的例子为:

class A
{
public:
    virtual void Funtest1()
    {
        cout << "A::Funtest1()" << endl; 
    }
    int _a;
};
class B
{
public:
    virtual void Funtest2()
    {
        cout<< "B::Funtest2() "<< endl;
    }
    int _b;
};
class C :public A, public B
{
public:
    virtual void Funtest1()
    {
        cout << "C::Funtest1()" << endl;
    }
    virtual void Funtest2()
    {
        cout << "C::Funtest2()" << endl;
    }
    virtual void Funtest3()
    {
        cout << "C::Funtest3()" << endl;
    }
    int _c;
};
int main()
{
    A a;
    B b;
    C c;
    c._c = 1;
    c._a = 2;
    c._b = 3;
return 0;
}

进入内存中可看到:
这里写图片描述
进入虚表中一探究竟,结果如下图。
这里写图片描述
则可知在多继承中,派生类继承几个基类就有几个虚表指针几个虚表,按继承的顺序排序(先继承的基类的虚表在上面,后继承在下面),然后把派生类自己新增加的虚函数加在第一张虚表的后面。
菱形继承
菱形继承的基本形式为:
这里写图片描述
但一般为了避免二义性,则用虚继承,所以下面我们来讨论虚继承的菱形继承。
具体代码如下:

class A
{
public:
    virtual void Funtest1()
    {
        cout << "A::Funtest1()" << endl;
    }
    virtual void Funtest2()
    {
        cout << "A::Funtest2()" << endl;
    }
    int _a;
};
class B: virtual public A
{
public:
    virtual void Funtest1()
    {
        cout << "B::Funtest1()" << endl;
    }
    virtual void Funtest3()
    {
        cout << "B::Funtest2()" << endl;
    }
    int _b;
};  
class C :virtual public A
{
public:
    virtual void Funtest1()
    {
        cout << "C::Funtest1()" << endl;
    }
    virtual void Funtest4()
    {
        cout << "C::Funtest2()" << endl;
    }
    int _c;
};
class D :public B, public C
{
public:
    virtual void Funtest1()
    {
        cout << "D::Funtest1()" << endl;
    }
    virtual void Funtest3()
    {
        cout << "D::Funtest3()" << endl;
    }
    virtual void Funtest4()
    {
        cout << "D::Funtest4()" << endl;
    }
    virtual void Funtest5()
    {
        cout << "D::Funtest5()" << endl;
    }
    int _d;
};
typedef void(*Func)();
void PrintVF(A&a)
{
    Func*pFun = (Func*)(*(int*)&a);
    while (*pFun)
    {
        (*pFun)();
        pFun++;
    }
}
void PrintVF(B&b)
{
    Func*pFun = (Func*)(*(int*)&b);
    while (*pFun)
    {
        (*pFun)();
        pFun++;
    }
}
void PrintVF(C&c)
{
    Func*pFun = (Func*)(*(int*)&c);
    while (*pFun)
    {
        (*pFun)();
        pFun++;
    }
}
int main()
{
        A a;
        B b;
        C c;
        D d;
        d._a = 1;
        d._b = 2;
        d._c = 3;
        d._d = 4;
        A&pa = d;
        B&pb = d;
        C&pc = d;
        PrintVF(pa);
        PrintVF(pb);
        PrintVF(pc);
        cout << sizeof(A) << endl;
        cout << sizeof(B) << endl;
        cout << sizeof(C) << endl;
        cout << sizeof(D) << endl;
        system("pause");
        return 0;
}

运行结果如下图:
这里写图片描述
那么对其内部的内存分析得出:
这里写图片描述
这里写图片描述
则我们可以看到在该菱形继承中先继承B类,再继承C类,即按继承的顺序排序(先继承的基类的虚表在上面,后继承在下面),然后把派生类B类将自己在类中的未重写的虚函数显示,C类将自己将自己在类中的未重写的虚函数显示。D类中新增加的虚函数加在第一张虚表的后面。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值