• C++中为什么要用虚函数、指针或引用才能实现多态?


    原文链接:http://blog.csdn.net/zoopang/article/details/14071779

    学过C++的都知道,要实现C++的多态性必须要用到虚函数,并且还要使用引用或者指针,以前学习的时候书本上也是这么说,

    但是书本上没有说为什么?   其实只要你认真思考过这个问题你会有三个疑问:

    为什么要用虚函数?

    为什么要用指针或者引用?

    为什么使用派生类和基类对象之间直接赋值不能实现??

    一个简单的例子:

    1. class A  
    2. {  
    3. public:  
    4.     virtual void print()  
    5.     {cout<<"A"<<endl;}  
    6. };  
    7.   
    8. class B:public A  
    9. {  
    10. public:  
    11.     void print()  
    12.     {cout<<"B"<<endl;}  
    13. };  
    14. int main()  
    15. {  
    16.    A a;  
    17.    B b;  
    18.     
    19.    A *pa = &b;//能实现多态  
    20.    pa->print();  
    21.     
    22.    a = b; //不能实现多态,为什么?  
    23.    a.print();  
    24.   
    25.    return 0;  
    26. }  

    进一步的,了解C++的人都应该知道只要有虚函数的类就会有一张虚函数表,多态就是通过这张表来实现的。

    所以,只要你不断探索下去,就会很快发现前面两个疑问豁然开朗,在这里,我主要探索第三个疑问。

    好了,我这里假设你已经对前面两个问题很清楚了。我们进一步把这个问题细化:

    程序执行a = b操作时,b中的虚表到底有没有赋给a?

    若赋给a了,是不是一定要用指针或引用才能通过虚表调用虚函数?

    下面我们通过一个小程序,探索一下:

    1. class A  
    2. {  
    3. public:  
    4.     virtual void T(){}  
    5.     virtual void print()  
    6.     {cout<<"A"<<endl;}  
    7. };  
    8. class B:public A  
    9. {  
    10. public:  
    11.     void print()  
    12.     {cout<<"B"<<endl;}  
    13. };  
    14. int main()  
    15. {  
    16.   A a;  
    17.   B b;  
    18.   a = b;  
    19.   a.print();                         //通过对象直接访问  
    20.   b.print();  
    21.   return 0;  

    首先我们通过调试来看看虚函数表:

    程序运行到a=b处时:

    我们可以看到B类(派生类)如果覆盖了A类(被继承类)的成员函数,则B类(派生类)的虚函数表第二项(*vtptr[1])的内容也会被覆盖,但是A类(被继承类)的虚函数表的其他表项依然被继承过来,表项的顺序和他们在A类(被继承类)的声明是一致(在这里我不再验证)。

    程序运行到a.print()处时:

    我们可以看到A类(被继承类)中的虚函数表项并没有被覆盖,也就说没有赋值过来,所以自然的使用a.print()时,也只是调用自己的成员函数。

    到这里我们找到了第一个疑问的答案。

    既然默认的赋值运算没有实现虚表的赋值,那么我们就重写A类的成员函数operaotor =:

    1. A& operator = (const B& b)  
    2.     {  
    3.         *(int *)this=*(int *)&b;  
    4.         return *this;  
    5.     }  

    添加了代码之后,我们继续来运行上面的代码,得到如图的运行结果:

    这说明,对象去访问成员的虚函数并不通过虚函数表。

    为了验证虚函数表的确传了过去,可以再添加下面两行代码:

    1. A *pa = &a;  
    2. pa->print();  

    输出如下:

    到这里为止,提出的疑问基本上已经有了答案。但是,又产生了新的疑问,对象为什么访问不了虚函数表?

    一句话解释:

    1.默认的赋值运算符并不会操作虚函数表。

    2.要实现多态,必须使用指针或者引用。

    后话:文章到此结束,对于后面那个疑问,博主我也不知道答案,如果你知道,麻烦留言告知一下,还有就是此文我只是想记录一下我探索的过程,其中可能会有错误,所以如果你发现了错误,请你一定要提出来,以免我误导其他读者,谢谢!

  • 相关阅读:
    课程的正确步调——Leo鉴书74
    poj(1011)——Sticks(经典的dfs+剪枝)
    怎样去除JSP页面提示:Cannot return from outside a function or method.
    SharePoint 2013 中代码创建列表查阅项字段
    王立平--GUI与GUILayout的差别
    zoj How Many Shortest Path
    QVector的内存分配策略
    java中基于TaskEngine类封装实现定时任务
    超越竞争对手的秘密武器-技术重用
    redis-2.6.16源码分析之pub-sub系统
  • 原文地址:https://www.cnblogs.com/johnnyflute/p/3692520.html
Copyright © 2020-2023  润新知