多态及多态的对象模型

1.什么多态? 

所谓多态,其实就是“多种形态”。C++中虚函数的作用主要就是实现多态。简单说父类的指针或者引用调用重写的虚函数,当父类指针或者引用指向父类对象时调用的是父类的虚函数,指向子类对象调用的是子类的虚函数。

虚函数表是通过一块连续内存来储存虚函数的地址。这张表解决了继承、虚函数(重写)的问题。在有虚函数的对象实例中都存在一个指向虚函数表的指针,虚函数表就像一张地图,指明实际应该调用哪个虚函数。

2.多态的对象模型--单继承&多继承? 

单继承:

#include<iostream>
using namespace std;
class Person
{
public:
	virtual void BuyTickets()
	{
		cout<<"买票"<<endl;
	}
};
class Student : public Person
{
public:
	virtual void BuyTickets()
	{
		cout<<"买票半价"<<endl;
	}
};
int main()
{
	Person p;
	Student s;
	Person *ptr;

	ptr = &p;
	p.BuyTickets();

	ptr = &s;
	s.BuyTickets();
	return 0;
}

运行结果:


我们可以看到:

(1)虚函数重写(函数名、参数、返回值都相同,协变除外)

(2)父类的指针或者引用调用虚函数

所以构成多态,故跟类型无关,而与对象有关。

探索单继承多态的对象模型:

#include<iostream>
using namespace std;
class A
{
public:
	virtual void f1()
	{
		cout<<"A::f1()"<<endl;
	}
	virtual void f2()
	{
		cout<<"A::f2()"<<endl;
	}
	int _a;
};
class B : public A
{
public:
	virtual void f1()
	{
		cout<<"B::f1()"<<endl;
	}
	virtual void f3()
	{
		cout<<"B::f3()"<<endl;
	}
	int _b;
};
int main()
{
	A a;
	B b;
	return 0;
}

 

调试窗口:

可以看到我们并看不到虚函数B::f3()是如何存放的,这是编辑器的优化导致的结果,但是我们想要弄明白它到底是怎样存储的,所以就需要我们自己手动打印虚函数表。

typedef void (*V_FUNC)();
void PrintVTable(int vtable)
{
	int* VfArray = (int*)vtable;
	printf("vtable:0x%p\n",VfArray);
	for(int i = 0;VfArray[i] != 0;++i)
	{
		printf("vfun[%d]:0x%p->",i,VfArray[i]);
		V_FUNC f = (V_FUNC)VfArray[i];
		f();
	}
	cout<<"*********************************"<<endl;
}
int main()
{
	A a;
	B b;
	PrintVTable(*((int*)&a));
	PrintVTable(*((int*)&b));
	return 0;
}

 

运行界面:


 

画出单继承的对象模型:

探索多继承多态的对象模型:

#include<iostream>
using namespace std;
class A
{
public:
	virtual void f1()
	{
		cout<<"A::f1()"<<endl;
	}
	virtual void f2()
	{
		cout<<"A::f2()"<<endl;
	}
	int _a;
};
class B  
{
public:
	virtual void f1()
	{
		cout<<"B::f1()"<<endl;
	}
	virtual void f2()
	{
		cout<<"B::f2()"<<endl;
	}
	int _b;
};
class C : public A,public B
{
public:
	virtual void f1()
	{
		cout<<"C::f1()"<<endl;
	}
	virtual void f3()
	{
		cout<<"C::f3()"<<endl;
	}
	int _c;
};
int main()
{
	A a;
	B b;
	C c;
	return 0;
}
调试窗口:

我们可以看到,在调试窗口中,我们看不到虚函数 C::f3() 是如何存放的,所以需要我们手动打印。

typedef void (*V_FUNC)();
void PrintVTable(int vtable)
{
	int* VfArray = (int*)vtable;
	printf("vtable:0x%p\n",VfArray);
	for(int i = 0;VfArray[i] != 0;++i)
	{
		printf("vfun[%d]:0x%p->",i,VfArray[i]);
		V_FUNC f = (V_FUNC)VfArray[i];
		f();
	}
	cout<<"*********************************"<<endl;
}
int main()
{
	A a;
	B b;
	C c;
	PrintVTable(*((int*)&c));
	PrintVTable(*(int*)((char*)&c+sizeof(A)));
	return 0;
}

运行界面:

画出多继承的对象模型:

 

3.多态的对象模型--菱形继承和菱形虚拟继承?

 

#include<iostream>
using namespace std;
class A
{
public:
	virtual void f1()
	{
		cout<<"A::f1()"<<endl;
	}
	virtual void f2()
	{
		cout<<"A::f2()"<<endl;
	}
public:
	int _a;
};
class B:public A
{
public:
	virtual void f1()
	{
		cout<<"B::f1()"<<endl;
	}
	virtual void f3()
	{
		cout<<"B::f3()"<<endl;
	}
public:
	int _b;
};
class C:public A
{
public:
	virtual void f1()
	{
		cout<<"C::f1()"<<endl;
	}
	virtual void f4()
	{
		cout<<"C::f4()"<<endl;
	}
public:
	int _c;
};
class D:public B,public C
{
public:
	virtual void f4()
	{
		cout<<"D::f4()"<<endl;
	}
public:
	int _d;
};
int main()
{
	D d;
	d.f1();
	return 0;
}

编译会出现错误:


这是因为D继承了B和C,而B和C都有一份从A继承下来的f1(),同时这里B和C对于D来说属于同一地位,所有导致对f1()访问不明确。

为解决这个错误我们可以指定f1()作用域,也可以在D内重写f1().

class D:public B,public C
{
public:
	virtual void f1()
	{
		cout<<"D::f1()"<<endl;
	}
	virtual void f4()
	{
		cout<<"D::f4()"<<endl;
	}
public:
	int _d;
};

这样就解决了f1()调用不明确的问题。

菱形继承的对象模型:

#include<iostream>
using namespace std;
class A
{
public:
	virtual void f1()
	{
		cout<<"A::f1()"<<endl;
	}
	virtual void f2()
	{
		cout<<"A::f2()"<<endl;
	}
public:
	int _a;
};
class B:public A
{
public:
	virtual void f1()
	{
		cout<<"B::f1()"<<endl;
	}
	virtual void f3()
	{
		cout<<"B::f3()"<<endl;
	}
public:
	int _b;
};
class C:public A
{
public:
	virtual void f1()
	{
		cout<<"C::f1()"<<endl;
	}
	virtual void f3()
	{
		cout<<"C::f3()"<<endl;
	}
public:
	int _c;
};
class D:public B,public C
{
public:
	virtual void f1()
	{
		cout<<"D::f1()"<<endl;
	}
	virtual void f4()
	{
		cout<<"D::f4()"<<endl;
	}
public:
	int _d;
};
typedef void (*VFUNC)();
void PrintVTable(void* vtable)
{
	printf("vtable:0x%p\n",vtable);
	VFUNC* array = (VFUNC*) vtable;
	for(int i = 0;array[i] != 0;++i)
	{
		printf("vtable[%d]:0x%p->",i,array[i]);
		array[i]();
	}
	cout<<"*********************************"<<endl;
}
int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	PrintVTable(*((int**)&d));
	PrintVTable(*((int**)((char*)&d+sizeof(B))));
	return 0;
}

运行结果:

对象模型:

菱形虚继承多态的对象模型:

 

#include<iostream>
using namespace std;
class A
{
public:
	virtual void f1()
	{
		cout<<"A::f1()"<<endl;
	}
	virtual void f2()
	{
		cout<<"A::f2()"<<endl;
	}
public:
	int _a;
};
class B: virtual public A
{
public:
	virtual void f1()
	{
		cout<<"B::f1()"<<endl;
	}
	virtual void f3()
	{
		cout<<"B::f3()"<<endl;
	}
public:
	int _b;
};
class C:virtual public A
{
public:
	virtual void f1()
	{
		cout<<"C::f1()"<<endl;
	}
	virtual void f4()
	{
		cout<<"C::f4()"<<endl;
	}
public:
	int _c;
};
class D:public B,public C
{
public:
	virtual void f1()
	{
		cout<<"D::f1()"<<endl;
	}
	virtual void f4()
	{
		cout<<"D::f4()"<<endl;
	}
public:
	int _d;
};
typedef void (*V_FUNC)();
void PrintVTable(int vtable)
{
	int* VfArray = (int*)vtable;
	printf("vtable:0x%p\n",VfArray);
	for(int i = 0;VfArray[i] != 0;++i)
	{
		printf("vfun[%d]:0x%p->",i,VfArray[i]);
		V_FUNC f = (V_FUNC)VfArray[i];
		f();
	}
	cout<<"*********************************"<<endl;
}
int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	PrintVTable(*((int*)&d));
	PrintVTable(*(int*)((char*)&d+sizeof(B)-sizeof(A)));
	PrintVTable(*(int*)((char*)&d+sizeof(B)+sizeof(C)-16+4));
	return 0;
}

运行结果

对象模型:

吐舌头
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值