所谓多态本质

来源:互联网 发布:多空对比资金指标源码 编辑:程序博客网 时间:2024/06/13 07:49
 请看代码!
#include <iostream>using namespace std;class people{    public:    people():age(0),name(""){}    people(int i,string s):age(i),name(s){}    virtual void sleep()    {        cout << "people sleep!" <<endl;    }    virtual ~people(){}    private:    int age;    string name;};class student:public people{    public:    student():people(),id(0){}    student(int a,string str,int b):people(a,str),id(b){}    void sleep()    {        cout << "student sleep!" << endl;    }    private:    int id;};int main(){    student ms(23,"x_zhang",1001);    //1    people mp = ms;    mp.sleep();    //2    people mp1;    mp1 = ms;    mp1.sleep();    //3    people *pp = &ms;    pp->sleep();    //4    people &pp1 = ms;    pp1.sleep();    return 0;}


这貌似是段看起来很无聊的代码,我想看过C++的都应该知道输出是什么了
关键不在于你知不知道结果而在于你知道为什么吗?为什么通过对象调用虚函数不能多态,而通过指针或者引用就产生多态呢?我初次学C++的时候我对这个问题真的是很纳闷,那个时候就安慰自己当公式记起来吧~,然后抱着这种思想搞C++那实在是痛苦,因为你一不懂就当公式记起来,那要记得太多了,记不死你...
好了好了,我就来讲讲为什么
因为虚函数。
“不要砸我,我才说了一句呢”
首先因为虚函数,
其次虚表
再次虚表指针
模型变化
间接和直接操作

从后面往前面讲:
间接和直接操作:也就是指针,引用和对象区别

int a(10);int *p = &a;int &b = a;

这里我们称直接操作a叫直接操作,通过指针p,和引用b来操作a叫间接操作,反应到汇编其实就是多加了一行代码,什么代码,那就是把a的地址保存起来了,然后通过这个地址在访问a占据的空间。

然后是模型变化

      //1    people mp = ms;    mp.sleep();//这里肯定很多人说slice down 不错,这种模型变化就是切割,但是这种切割额外的开辟了栈空间,sizeof(mp)    //2    people mp1;    mp1 = ms;    mp1.sleep();//同上,切割    //3    people *pp = &ms;    pp->sleep();//没有开辟额外sizeof(mp)的空间,只是开辟了4个字节保存了ms首地址    //4    people &pp1 = ms;    pp1.sleep();//同上//以上全是模型的切割变化,为什么切割,因为这里是派生类到基类的变化,派生类对象大些,当然要切割,切掉派生类自己的那部分。这个模型变化还是很简单的


再说虚表指针,虚表,虚函数
这个说起来就罗嗦了
首先一个类只要有虚函数就有一张虚表,虚指针是依附于对象而存在的,在对象构造的时候就会按插一个虚指针并且用类虚表的首地址初始化它。打住,知道这些就够了,不懂的可以在草纸画画图

再说四大谜之函数,其实这里只说三大,构造,复制构造和赋值函数
只要是对象,不管他怎么来的(无外乎构造,复制构造,赋值而来),他其中的虚表指针就指向它本类的虚表
他本类的虚表是基类的虚表当然不会调用派生类的虚函数了,到此我们解释了通过对象不能实现多态。
--------------------------------------------------------------------------------------------
再谈通过指针和引用:
间接操作,模型替换,
people *pp = &ms;
这几天我为好几个帖子翻译了这句话的意思,这就是模型替换,基类模型替换派生类。只要声明一个指针,那么这个指针所指向的内存空间大小就已经确定了,这里是sizeof(people),这里ms占的空间要大,pp所指向的空间不需要那么大,超出的部分pp是访问不了的,这句话的意思也就是,将ms的首地址赋值给pp,pp指向以ms的首地址为起始地址sizeof(people)的内存空间,仅仅就做了这么多,没有更多
那么这个pp指向的空间是怎么布局的呢,那就和对象的布局是一样的,首先是vptr,其次是non-static data,
那么我们看看pp->sleep();这句发生了什么?
首先pp去以ms为首地址的4个字节空间里访问vptr的值,哎呀见鬼了,这个值是派生类的vptr的值,但是编译器不会管有没有问题,因为空间可以访问,通过这个值找到了派生类的虚表,然后通过虚表找sleep这个虚函数,这是派生类的虚表自然是调用派生类的虚函数了。


哎呀,这就是所谓的多态了,活见鬼了,这是编译器的将错就错,结果却成了面形对象的基础,非常有意思哈


原创粉丝点击