• 虚拟函数和虚拟继承


     什么是虚拟函数,我想如果你能马上用口头表达出来,那么你的基础不错。知道虚拟函数表吗?也许你已经看了很多书,了然于胸。其实很多时候并不需要看书,就可以体会到。然后什么是虚拟继承?也许听说过,但很少使用。其实使用虚拟继承,可以减轻你很多的痛苦。
      
       看下面一个例子:
      
       这里的例子尽可能的简单,关于虚拟函数。
     
    class Father
    {
    public :
       
        Father();
     virtual void run();
     virtual void say();
    private:
     static int i;

    };

    int Father::i=2;

    Father::Father()
    {
     this->run();
    }

    void Father::run()
    {
     cout<<this->i<<endl;
            this->say();
    }
    void Father::say()
    {
     cout<<"Father"<<endl;
    }
    class Child : public Father
    {
    public:
     
     Child();
     virtual void say();
    private:
     static int i;

    };
          注意,为了避免歧义,int i已经是private 类型了。

    int Child::i=1;

    Child::Child() : Father()
    {
       
    };


    void Child::say()
    {
     cout<<"Child"<<endl;
    }

    下面的测试函数:

        int main()
    {
     Father x;
     Child y;
     x.run();
     y.run();
    }

    结果是什么?
       
        有经验的人可能一眼就看出来了,我想,两个say,应该都没有问题。然后两个构造函数,他输出的是什么?
        可能有人说,virtual void run();不是虚拟的吗?所以Child y调用构造函数时,当然输出的是1.
        但马上就会有人反驳,应该是0?,因为Child::Child() : Father(),在构造Father()的时候,Child()还没有构造,当然,Child::i 的值不可能是1。
        其实,结果不是这样,输出的是两个2!为什么? 虚拟函数失效了吗? 其实不是的,原因很简单,你要了解到i可不是虚拟的!而为什么是输出的是两个father?这里有两种说法,1:在构造father的时候,child没有构造,包括应该继承的虚拟函数,2:因为father()本身就是father类的函数。所以,调用father类的方法。 其实这两种说法是一致的。

        下一个例子你会更加明白。这是一个完整的例子:
       
    #include <iostream>
    using namespace std;

    class Father
    {
    public :
       
        Father();
        virtual void sayj();
    protected:
     int j;

    };

    void Father::sayj()
    {
     cout<<j<<endl;
    }

    class Child : public Father
    {
    public:
     
     Child();
    protected:
     int j;

    };


    Child::Child() : Father()
    {
     this->j=10;

    };

    Father::Father()
    {
     this->j=100;
    };

    int main()
    {
     Father x;
     Child y;
     y.sayj();
    }

         结果是多少?100 而不是10, 这里,构造函数完成工作后,再调用sayj,他调用的的确是Child->sayj没错,是从Father继承过来的。但是,同样j不是虚拟的,由于Child中没有重写sayj,所以它会到“父类”中去找!一定要记住,虚拟函数不是宏,它很智能,它知道sayj来自父类,所以j也来自父类。

         有时候只要仔细一些这些小的错误,就可以避免。
         现在就出一道题目了,看看你是否理解了e。其实就是第一个例子的题目:

    #include <iostream>
    using namespace std;
    class Father
    {
    public :
       
        Father();
     virtual void run();
     virtual void say();
    private:
     static int i;

    };

    int Father::i=2;

    Father::Father()
    {
     this->run();
    }

    void Father::run()
    {
     cout<<this->i<<endl;
            this->say();
    }
    void Father::say()
    {
     cout<<"Father"<<endl;
    }
    class Child : public Father
    {
    public:
     
     Child();
     virtual void say();
    private:
     static int i;

    };

    int Child::i=1;

    Child::Child() : Father()
    {
       
    };


    void Child::say()
    {
     cout<<"Child"<<endl;
    }


        int main()
    {
     Father x;
     Child y;
     x.run();
     y.run();
    }
    结果是多少?static 是故意迷惑你的。

    结果是
    2
    Father
    2
    Father
    2
    Father
    2
    Child

    对了吗?

    为什么最后一个是"Child"? 因为say 在 Child 中重写过了!记住它很智能。

       总结一下,在父类没有构造好前,子类不会构造,包括应该继承的虚拟函数!
     即使构造了虚拟函数,虚拟函数不是宏!它做的不是简单的替换。
    类变量不是虚拟的,构造函数不是虚拟的。(原理很简单,只是有时候其他比较有趣的理论也讲的通,增加了我们理解本质的难度)

    好下面来说说虚拟继承。如果你了解了虚拟函数那么虚拟继承也就不难了。

    很简单的例子。

    class CWinBase : virtual protected IGetProc

    class CPaint : virtual protected IGetProc,public CWinBase

    好如果不用虚拟继承,那么IGetProc 类,会出现什么问题? 那就是ambiguous. 因为CWinBase中已经有了一个IGetProc了。(上面两个定义的方式并不好,不要学习。)这样做只是为了说明问题。为什么可以除去ambiguous,其实原理和虚拟函数是一样的。所谓虚拟,也就是当真正运行的时候,只有一个实体。原理嘛,就去看看Professional C++ ,里面说的很详细。


       有时给自己一点提醒。不会犯可笑的错误

  • 相关阅读:
    U盘修复
    在tomcat中配置jdk的不同版本
    集​群​t​o​m​c​a​t​+​a​p​a​c​h​e​配​置​文​档
    如何挂自己的web项目(免费拥有自己的网站及域名)
    JQuery对表格进行排序
    JQuery中对各种域进行隐藏和显示操作
    大文件批量上传断点续传文件秒传
    web 开发常用字符串表达式匹配
    spring 工具类大集合
    spring 是如何注入对象的和bean 创建过程分析
  • 原文地址:https://www.cnblogs.com/buffer/p/1577845.html
Copyright © 2020-2023  润新知