C++学习:虚函数

本文详细探讨了C++中的多态性,包括重载多态、包含多态、参数多态和强制多态。通过实例解释了如何使用虚函数实现包含多态,展示了如何通过指针调用虚函数表来调用相应的函数。同时,讨论了同名同参的虚函数在类继承中的覆盖行为,以及多继承情况下虚函数的调用。最后,阐述了联编的概念,区分了早联编和晚联编,并通过代码示例解释了晚联编在多态调用中的作用。
摘要由CSDN通过智能技术生成

多态:
1.重载多态----函数重载,运算符重载
2.包含多态----virtual函数
3.参数多态----模板
4.强制多态----强制类型转换

对(FUN)* int* *int * &a 的理解

class A
{
public:
	virtual void fa() { cout << "A::fa" << endl; }
	virtual void fb() { cout << "A::fb" << endl; }
	virtual void fc() { cout << "A::fc" << endl; }
};

int main()
{
	cout << sizeof(A);//4
}

如果一个类包含了虚函数,不管有多少个虚函数,只增加了一个指针的大小,
多了一个指针VPtr,指向虚表,虚表里面存储虚表中虚函数的入口地址。

   A a;
	typedef void(*FUN)();//声明一个函数指针
	FUN pf= NULL;//声明了一个函数pf,FUN pf; /*等价于void pf();*/
	pf = (FUN) * ((int*)(*(int*)(&a)));//a.fa();
	pf();
	pf = (FUN) * ((int*)(*(int*)(&a)) + 1);//a.fb();
	pf();
	pf = (FUN) * ((int*)(*(int*)(&a)) + 2);//	a.fc();
	pf();

1、(int )(&b) 表示对象b中指向虚函数table的指针的地址。
2、
((int )(&b)) 表示对象b中指向虚函数table的指针,即虚函数table的首地址。
3、(int )(((int )(&b)))+0 表示对象b中指向虚函数table的第一个表项的地址,注意该表项的内容为第一个虚函数的地址。
4、
((int )(((int )(&b)))+0) 表示对象b中指向虚函数table的第一个表项的内容----即第一个虚函数的地址。
将其赋值给 pFun,便可以调用第一个虚函数,pFun = (Fun)(
((int )(((int )(&b)))+0) )

存在同名同参的函数


注意这里A类和B类的成员中都有名为fa()的同名同参函数

class A
{
	public:
		virtual void fa() { cout << "A::fa" << endl; }
		virtual void ga() { cout << "A::ga" << endl; }
		virtual void ha() { cout << "A::ha" << endl; }
};

class B :public A
{
public:
	virtual void fa() { cout << "B::fa" << endl; }//同名同参的虚函数,覆盖
	virtual void gb() { cout << "B::gb" << endl; }
	virtual void hb() { cout << "B::hc" << endl; }
};

void test(A a)
{
	a.fa();
}

int main()
{
	B b;
	typedef void(*FUN)();
	FUN pf = NULL;
	pf = (FUN) *( (int*)*(int*)(&b));
	pf();
	pf = (FUN) * ((int*)*(int*)(&b)+1);
	pf();
	pf = (FUN) * ((int*)*(int*)(&b) + 2);
	pf();
	pf = (FUN) * ((int*)*(int*)(&b) + 3);
	pf();
	pf = (FUN) * ((int*)*(int*)(&b) + 4);
	pf();
}

在这里插入图片描述
为什么第一行应该输出的是A::fa为什么会变成B::fa呢?
因为子类B拥有和基类A同名同参的fa函数,而在B类中重写并覆盖原来的A::fa()

存在多继承类的虚函数

{
public:
    virtual void fa() { cout << "A::fa" << endl; }
    virtual void ha() { cout << "A::ha" << endl; }
};
class B
{
public:
    virtual void fb() { cout << "B::fb" << endl; }
    virtual void hb() { cout << "B::hb" << endl; }
};
class C
{
public:
    virtual void fc() { cout << "C::fc" << endl; }
    virtual void hc() { cout << "C::hc" << endl; }
};
class D :public A, public B, public C
{
public:
    virtual void fd() { cout << "D::fd" << endl; }
    virtual void hd() { cout << "D::hd" << endl; }
};

void main()
{
    cout << sizeof(D) << endl;
    D d;
    typedef void(*FUN)();
    FUN pf = NULL;
    pf = (FUN) * ((int*)*(int*)(&d));
    pf();
    pf = (FUN) * ((int*)*(int*)(&d) + 1);
    pf();
    pf = (FUN) * ((int*)*(int*)(&d) + 2);
    pf();
    pf = (FUN) * ((int*)*(int*)(&d) + 3);
    pf();
    pf = (FUN) * ((int*)*(int*)((int*)(&d) + 1));
    pf();
    pf = (FUN) * ((int*)*(int*)((int*)(&d) + 1)+1);
    pf();
    pf = (FUN) * ((int*)*(int*)((int*)(&d) + 2));
    pf();
    pf = (FUN) * ((int*)*(int*)((int*)(&d) + 2) + 1);
    pf();
}

在这里插入图片描述

联编


覆盖(重写)
1.最少两个类,必须是父子关系
2,同名同参虚函数,基类写了virtual,子类可以不写

多态的条件:
1.满足覆盖
2.基类的指针或者引用指向基类对象或派生类对象

联编(捆绑,绑定)–——函数调用和函数体联系的过程
早联编–编译时
晚联编–运行时

class A
{
public:
	virtual void fn() { cout << "A::fn" << endl; }
};
class B :public A
{
public:
	virtual void fn() { cout << "B::fn" << endl; }
};
void test(A aa)//(A &aa)引用传递则可以输出b.fn
{
	aa.fn();//
}
void test1(A *p)
{
	p->fn();
}
int main()
{
	A a;
	B b;
	a.fn();
	b.fn();
	test(a); 
	test(b);//为什么输出不是“b.fn"呢,因为函数test()里参数A aa已经和fn函数捆绑
   test1(&a);
	test1(&b);
}

在这里插入图片描述

为什么test(b)时输出的不是B::fn(),而是A::fn()?
因为函数test()里形参A aa已经和fn函数捆绑,默认传过来的值都是A类型的,所以调用的是A类的fn函数。

解决办法:
因为test(a)和test(b)都是值传递,所以可以采取引用传递 test1(&a)和test1(&b)来达到目的
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值