虚函数、继承、多重继承、动态与静态编译

虚函数、继承、多重继承

虚函数本质

为啥要用虚函数呢,那先分析下,如果不用虚函数会怎样:
class cBase{
public:
	cBase();
    ~cBase();
    void fn1();
    int fn2();
    void fn3();
};
class cDeriveA: public cBase{
	public:
		cDeriveA();
		~cDeriveA();
		void fn1();
		voif derAfn1();
}
class cDeriveB: public cBase{
	public:
		cDeriveB();
		~cDeriveB();
		voif derBfn1();
}
如此定义后,在定义cDeriveA或cDeriveB的实例时,实例中,CBase的所有函数都会有。
弊端是:
1:空间较大
2:不利于某些情况下多态实现,如定义:cBase *ptr = (cBase) new cDeriveA;
	后续ptr->fn1(),实际调用的是cBase中的fn1(),而不是cDeriveA中的(做个函数验证下)。

优点也有啊:
1:实现简单
2:如果cBase类比较小,存储空间和实现效率有一定优势。
综上的话,我们看如果用虚函数会怎样呢
class cBase{
public:
	cBase();
    virtual ~cBase();
    virtual void fn1();
    virtual int fn2();
    void fn3();
};
class cDeriveA: public cBase{
	public:
		cDeriveA();
		~cDeriveA();
		virtual void fn1();
		virtual voif derAfn1();
}
如上,如果CBase中定义了虚拟函数,那么cDeriveA只是继承了cBase的实函数,那虚函数呢?
一般的编译器操作是:基类有个虚函数表(virtual tables),将相关虚函数放置到表中,这样cDeriveA和cDeriveB都继承了cBase,但cBase里面的虚函数只有一个vtbl。这样的优点:
1:节省了内存空间
2:效率和重载实函数基本一样(主要是一个指针调用)
限制:
1:虚函数不要做成inline,因为inline本质是编译的时候将函数代码实际填充到函数调用处,而多态实现的时候,new 子类的时候还不知道具体是cDeriveA还是cDeriveB,所以无法使用。

虚拟类

既然有虚拟函数,那也就有虚拟类,类似如下行为:
class A {...};
class B: virtual class A {...};
class C: virtual class A {...};
class D: public B, public C {...};

继承虚拟类的B和C实际上都有一个指向基类的指针(pointer to virtual base class)。同时,结合上述,还有一个vptr(实例的指针,指向基类的虚函数table)。

这样的好处和类的虚函数基本一样,就是基类A只有一个实例,B和C都有个指针指向它,如此省空间。

多态的实现方式

多态的两种实现方式,
静态联编:编译期确定,主要是运算符重载和函数重载
动态联编:当子类重新定义了父类的虚函数后,父类指针根据赋值给它的不同的子类指针,动态的调用属于子类的该函数,这样函数调用在编译期间是无法确定的,这样的函数地址在运行期间绑定称为动态联编.

本质上就是编译阶段,编译器是否能确定类函数位置,可以就是静态,不能就是动态。
class cBase{
public:
	cBase();
    ~cBase();
    void fn1();
    vitual int fn2();
    void fn3();
};
class cDeriveA: public cBase{
	public:
		cDeriveA();
		~cDeriveA();
		void fn1();
		vitual int fn2();
		voif derAfn1();
};
class cDeriveB: public cBase{
	public:
		cDeriveA();
		~cDeriveA();
		vitual int fn2();
		voif derAfn1();
};
如上,如果是静态的编译,如直接cDeriveA *ptr = new cDeriveA.
那么就是确定的,ptr->fn1()调用的就是cDeriveA中的fn1,
如果动态编译,cBase *ptr = (cBase *)new cDeriveA.
此时调用ptr->fn1(),实际就是调用cBase中的fn1,而ptr->fn2()调用的是cDeriveA中的fn1().
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术的微光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值