虚拟函数和虚拟继承

 什么是虚拟函数,我想如果你能马上用口头表达出来,那么你的基础不错。知道虚拟函数表吗?也许你已经看了很多书,了然于胸。其实很多时候并不需要看书,就可以体会到。然后什么是虚拟继承?也许听说过,但很少使用。其实使用虚拟继承,可以减轻你很多的痛苦。
  
   看下面一个例子:
  
   这里的例子尽可能的简单,关于虚拟函数。
 
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++ ,里面说的很详细。


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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值