Cpp 对象模型探索 / 继承关系下的虚函数手动调用

本文深入探讨C++中多态的实现原理,通过代码示例解析虚函数表的工作机制,展示子类如何通过重写父类虚函数实现多态。同时,分析对象切割现象及其对多态的影响。

一、多态机理

#include <iostream>

class Father
{
public:
    virtual void Func1()
    {
        std::cout << "Fahter::Func1()" << std::endl;
    }
    virtual void Func2()
    {
        std::cout << "Fahter::Func2()" << std::endl;
    }
    virtual void Func3()
    {
        std::cout << "Fahter::Func3()" << std::endl;
    }

private:
    size_t count_;
};

class Son : public Father
{
public:
    virtual void Func2()
    {
        std::cout << "Son::Func2()" << std::endl;
    }
};

using FUNC = void (*)();

int main()
{
    Father *pfather = new Father;
    Son *pson = new Son;

    // 得到指向虚函数表指针的地址。
    long *pvptr_father = (long *)pfather;
    long *pvptr_son = (long *)pson;

    // 得到指向虚函数表的指针。    
    long *vptr_father = (long *)(*pvptr_father); 
    long *vptr_son = (long *)(*pvptr_son); 

    FUNC func1_father, func2_father, func3_father;
    func1_father = (FUNC) * (vptr_father + 0); // 得到虚函数 Father::Func1() 的地址。
    func2_father = (FUNC) * (vptr_father + 1); // 得到虚函数 Father::Func2() 的地址。
    func3_father = (FUNC) * (vptr_father + 2); // 得到虚函数 Father::Func2() 的地址。

    func1_father();
    func2_father();
    func3_father();

    std::cout << std::endl;

    FUNC func1_son, func2_son, func3_son;
    func1_son = (FUNC) * (vptr_son + 0); // 得到虚函数 Father::Func1() 的地址。
    func2_son = (FUNC) * (vptr_son + 1); // 得到虚函数 Son::Func2() 的地址。
    func3_son = (FUNC) * (vptr_son + 2); // 得到虚函数 Father::Func2() 的地址。

    func1_son();
    func2_son();
    func3_son();
    return 0;
}

结果:

Fahter::Func1()
Fahter::Func2()
Fahter::Func3()

Fahter::Func1()
Son::Func2()
Fahter::Func3()

二、分析

       上图介绍了C++多态的实现机理。注意:虚函数表属于,虚函数表指针属于对象

       子类继承父类时,会得到和父类相同的虚函数表,该表记载着代码区中属于该类的虚函数的起始地址。当子类没有重写虚函数时,程序调用子类的虚函数,就相当于调用父类的虚函数。当子类重写了父类的虚函数时,相当于将子类的虚函数表中的相应虚函数的地址修改为子类虚函数的首地址,从而完成了多态!

三、对象切割

Son sn;
Father fa = sn;
fa.Func1();
fa.Func2();
fa.Func3();

结果:

Fahter::Func1()
Fahter::Func2()
Fahter::Func3()

       对于多态,使用指针或者引用,用父类调用子类的函数达到工厂模式的目的。倘若用子类直接赋值给父类,会有什么结果呢。上述代码就展示了最后的结果,并没有调用子类的函数,而是直接调用了父类的函数。

       原因是直接用子类给父类对象赋值,子类中的属于父类部分内容会被编译器自动切割出来并拷贝给了父类

       实际上上述操作一共干了两件事情,

       1、生成 Father 对象。

       2、用 sn 来初始化 Father 对象。显然,编译器并没有将 sn 的虚函数表指针覆盖掉 fa 的虚函数表指针。

四、总结

1、类只有包含了虚函数才存在虚函数表(只有一个),同属于一个类的对象共享虚函数表,但是有各自的vptr(虚函数表指针)。

2、父类含有虚函数,子类就会有一个虚函数表,但是子类的虚函数表只是和父类的虚函数表中的内容相同,所在的内存位置不同。

3、多态是子类赋值给父类的指针或者引用。若直接对象赋值,是对象切割的范畴。

 

(SAW:Game Over!)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值