C++中多态的几大关键点

1、什么是运行期多态?

在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。 给出一个简单的代码:

#include<iostream>
using  namespace std ;

class CBase
{
public:
	void FunTest1(int _iTest)
	{
		cout << "CBase::FunTest1()" << endl;
	}
	virtual void FunTest3(int _iTest)
	{
		cout << "CBase:: FunTest3()" << endl;
	}

	virtual void FunTest4(int _iTest)
	{
		cout << "CBase:: FunTest4()" <<endl;
	}

};

class CDerived: public CBase
{
	void FunTest1(int _iTest)
	{
		cout << "CDerived::FunTest1()" << endl;
	}

	virtual void FunTest3(int _iTest)
	{
		cout << "CDeived:: FunTest2()" << endl;
	}

	void FunTest4(int _iTest)
	{
		cout << "CDerived:: FunTest3()" << endl;
	}
};


void FunTest(CBase& cobj)
{
	cobj.FunTest1(1);
	cobj.FunTest3(1);
	cobj.FunTest4(1);
}



int main()
{
	CBase c;
	cout << "父类对象去调用函数" <<endl;
	FunTest(c);  //父类对象
	cout << endl;
	cout << "子类对象去调用函数" <<endl;
	CDerived d;  //子类对象
	FunTest(d);

	cout << "hello..." <<endl;
	system("pause");
	return 0;
}


结果分析:我们发现不管通过子类对象还是父类对象做参数,对FunTest1()函数的调用都是调用父类的的函数,没有表现出多态,但是其他两个函数却能调用各自的函数,这就是多态。这里就简单的总结下多态发生的三个条件:

1)、要有继承

2)、父类的虚函数要在子类中重写。

3)、父类的指针或引用指向父类对象或子类对象。


2 多态是怎么实现的?

对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针 ,子类对象和父类对象都有各自的虚表,虚表存放的是各自虚函数的入口地址,当通过基类的指针或引用调用虚函数时,系统会通过动态对象的指针找到对应的虚表,拿到相对应的函数的入口地址,实现多态。


3、通过前面的简单的描述,我们能看出多态的实现就是靠虚表,现在我们就来剖析下虚表:

一:
(1).单继承:
#include<iostream>
using  namespace std ;


class B
{
public:
	virtual void Funtest1()
	{
		cout << "B::Funtest1()" << endl;
	}
	virtual void Funtest2()
	{
		cout << "B::Funtest2()" << endl;
	}
public:
	int _data1;
};

class D:public B
{
public:
	virtual void Funtest1()
	{
		cout << "D::Funtest1()" << endl;
	}
	virtual void Funtest3()
	{
		cout << "D::Funtest3()" << endl;
	}
	int _data2;
};


void test0()
{

	B b1;
	b1._data1 = 1;
	cout << "sizeof(b1)=" << sizeof(b1) << endl;

	typedef void(*PVF)();     //虚表中存放的都是无返回,无参数的函数,定义一个这种类型的函数指针去指向它们。
	PVF *pvf = (PVF*)*(int*)&b1;//对象的前四个字节存放一个指针,这个指针指向一个虚表,虚表中存放的都是函数入口地址。
	//这一句就相当于一个函数指针类型的指针去指向一个函数指针类型的数组。
	cout << "基类虚表:" <<endl;
	while (*pvf)
	{
		(*pvf)();  //通过这个循环打印出虚表中存放的函数。
		pvf++;
	}
	cout << endl;

	D d1;
	d1._data1 = 2;
	d1._data2 = 3;
	cout << "sizeof(d1)=" << sizeof(d1) << endl;

	
	pvf = (PVF*)*(int*)&d1;//对象的前四个字节存放一个指针,这个指针指向一个虚表,虚表中存放的都是函数入口地址。
	//这一句就相当于一个函数指针类型的指针去指向一个函数指针类型的数组。

	cout << "派生类虚表:"<<endl;
	while (*pvf)
	{
		(*pvf)();  //通过这个循环打印出虚表中存放的函数。
		pvf++;
	}
}



int main()
{
	test0();

	
	system("pause");
	return 0;
}

单继承中派生类的成员数据模型:


说明:派生类的虚表中虚函数的安排:首先拷贝一份基类的虚函数表,看派生类中那个虚函数重写了,重写的话用派生类自己的虚函数代替基类的虚函数,最后再将自己的虚函数放在虚表的最后面。

(2)多继承:

#include<iostream>
using  namespace std ;




class C1
{
public:
	virtual void Funtest1()
	{
		cout << "C1::Funtest1()" << endl;
	}
	virtual void Funtest2()
	{
		cout << "C1::Funtest2" <<endl;
	}

public:
	int _data2;
};



class C2
{
public:
	virtual void Funtest3()
	{
		cout << "C2::Funtest1()" << endl;
	}
	virtual void Funtest4()
	{
		cout << "C2::Funtest3" <<endl;
	}
public:
	int _data3;
};

class D:public C1,public C2
{
public:
	virtual void Funtest4()
	{
		cout << "D::Funtest3()" << endl;
	}
	virtual void Funtest2()
	{
		cout << "D::Funtest2" <<endl;
	}

	virtual void Funtest7()   //派生类的虚函数。
	{
		cout << "D::Funtest7" <<endl;
	}
	virtual void Funtest8()  //派生类的虚函数。
	{
		cout << "D::Funtest8" <<endl;
	}
public:
	int _data4;
};

void test()
{
	cout << "sizeof(D):" << sizeof(D) << endl;  //sizeof(D):28   c1 12,+c2 12,+d 4   = 28
	D d1;

	cout << "&d1" << &d1 <<endl;  //看下派生类的地址。
	
	d1._data2 = 2;
	d1._data3 = 3;
	d1._data4 = 4;

	C1 &c1 = d1;

	cout << "&c1" << &c1 <<endl;  //看下基类去引用一个派生类,基类的地址。

	typedef void (*PVF)();
	PVF *pvf =  (PVF*)*(int*)&c1; //这就相当于一个二级指针指向一个指针数组。
	cout << "c1 virtural table:" << endl;

	while (*pvf)
	{
		(*pvf)();
		pvf++;
	}
	cout  << endl << endl;


	C2 &c2 = d1;  //c2的地址就是d1偏移C1类型的地址。
	cout << "&c2" << &c2 <<endl;  //看下基类去引用一个派生类,基类的地址。

	pvf =  (PVF*)*(int*)&c2; //这就相当于一个二级指针指向一个指针数组。

	cout << "c2 virtural table:" << endl;
	while (*pvf)
	{
		(*pvf)();
		*pvf++;
	}

	cout  << endl;
}

int main()
{

	test();
	
	
	cout << "hello..." <<endl;
	system("pause");
	return 0;
}



说明:多继承的时候派生类中有两张虚表,这里先继承C1,在继承C2,因此第一张虚表存放的是从C1拷贝过来虚表,如果子类有虚函数重写,将用重写的虚函数代替原来的虚函数,通过结果发现派生类的虚函数也放在第一张虚表中,第二张虚表存放的是从C2拷贝过来的虚表,同样也是用重写的虚函数代替原来的虚函数。


(3)菱形继承:

#include<iostream>
using  namespace std ;

class B
{
public:
	virtual void Funtest1()
	{
		cout << "B::Funtest1()" << endl;
	}
public:
	int _data1;
};

class C1:public B
{
public:
	virtual void Funtest1()
	{
		cout << "C1::Funtest1()" << endl;
	}
	virtual void Funtest2()
	{
		cout << "C1::Funtest2" <<endl;
	}

public:
	int _data2;
};



class C2:public B
{
public:
	virtual void Funtest1()
	{
		cout << "C2::Funtest1()" << endl;
	}
	virtual void Funtest3()
	{
		cout << "C2::Funtest3" <<endl;
	}
public:
	int _data3;
};

class D:public C1,public C2
{
public:
	virtual void Funtest3()
	{
		cout << "D::Funtest3()" << endl;
	}
	virtual void Funtest4()
	{
		cout << "D::Funtest4" <<endl;
	}
public:
	int _data4;
};

void test()
{
	cout << "sizeof(D):" << sizeof(D) << endl;  //sizeof(D):28   c1 12+c2 12+d 4   = 28
	D d1;
	d1.C1::_data1 = 0;
	d1.C2::_data1 = 1;
	d1._data2 = 2;
	d1._data3 = 3;
	d1._data4 = 4;

	C1 &c1 = d1;

	typedef void (*PVF)();
	PVF *pvf =  (PVF*)*(int*)&c1; //这就相当于一个二级指针指向一个指针数组。
	cout << "c1 virtural table:" << endl;

	while (*pvf)
	{
		(*pvf)();
		pvf++;
	}
	cout  << endl << endl;


	C2 &c2 = d1;
	pvf =  (PVF*)*(int*)&c2; //这就相当于一个二级指针指向一个指针数组。

	cout << "c2 virtural table:" << endl;
	while (*pvf)
	{
		(*pvf)();
		*pvf++;
	}

	cout  << endl;
}


int main()
{

	test();

	cout << "hello..." <<endl;
	system("pause");
	return 0;
}



说明:菱形继承的时候派生类中有两张虚表,这里先继承C1,在继承C2,因此第一张虚表存放的是从C1拷贝过来虚表,如果子类有虚函数重写,将用重写的虚函数代替原来的虚函数,通过结果发现派生类的虚函数也放在第一张虚表中,第二张虚表存放的是从C2拷贝过来的虚表,同样也是用重写的虚函数代替原来的虚函数。菱形继承有二义性,都继承了B的数据成员。


二:虚继承

(1)单继承

#include<iostream>
using  namespace std ;


class B
{
public:
	virtual void Funtest1()
	{
		cout << "B::Funtest1()" << endl;
	}
	virtual void Funtest2()
	{
		cout << "B::Funtest2()" << endl;
	}
public:
	int _data1;
};

class D:virtual public B
{
public:
	D()
	{

	}
	virtual void Funtest1()
	{
		cout << "D::Funtest1()" << endl;
	}
	virtual void Funtest3()
	{
		cout << "D::Funtest3()" << endl;
	}
	int _data2;
};


void test0()
{


	D d1;
	d1._data1 = 2;
	d1._data2 = 3;
	cout << "sizeof(D)=" << sizeof(D) << endl;

	cout << "继承过来的基类的虚函数的虚表:"<<endl;
	B &b1 = d1;
	typedef void (*PVF)();
	PVF*pvf = (PVF*)*(int*)&b1;
	while (*pvf)
	{
		(*pvf)();  //通过这个循环打印出虚表中存放的函数。
		pvf++;
	}
	cout << endl;

	
	pvf = (PVF*)*(int*)&d1;//对象的前四个字节存放一个指针,这个指针指向一个虚表,虚表中存放的都是函数入口地址。
	//这一句就相当于一个函数指针类型的指针去指向一个函数指针类型的数组。

	cout << "派生类自己新增的虚函数构成的虚表:"<<endl;
	while (*pvf)
	{
		(*pvf)();  //通过这个循环打印出虚表中存放的函数。
		pvf++;
	}
	cout <<endl;
}



int main()
{
	test0();

	
	system("pause");
	return 0;
}

  1. }  
#include<iostream>
using  namespace std ;


class B
{
public:
	virtual void Funtest1()
	{
		cout << "B::Funtest1()" << endl;
	}
	virtual void Funtest2()
	{
		cout << "B::Funtest2()" << endl;
	}
public:
	int _data1;
};

class D:virtual public B
{
public:
	D()
	{

	}
	virtual void Funtest1()
	{
		cout << "D::Funtest1()" << endl;
	}
	virtual void Funtest3()
	{
		cout << "D::Funtest3()" << endl;
	}
	int _data2;
};


void test0()
{


	D d1;
	d1._data1 = 2;
	d1._data2 = 3;
	cout << "sizeof(D)=" << sizeof(D) << endl;

	cout << "继承过来的基类的虚函数的虚表:"<<endl;
	B &b1 = d1;
	typedef void (*PVF)();
	PVF*pvf = (PVF*)*(int*)&b1;
	while (*pvf)
	{
		(*pvf)();  //通过这个循环打印出虚表中存放的函数。
		pvf++;
	}
	cout << endl;

	
	pvf = (PVF*)*(int*)&d1;//对象的前四个字节存放一个指针,这个指针指向一个虚表,虚表中存放的都是函数入口地址。
	//这一句就相当于一个函数指针类型的指针去指向一个函数指针类型的数组。

	cout << "派生类自己新增的虚函数构成的虚表:"<<endl;
	while (*pvf)
	{
		(*pvf)();  //通过这个循环打印出虚表中存放的函数。
		pvf++;
	}
	cout <<endl;
}



int main()
{
	test0();

	
	system("pause");
	return 0;
}

说明:从基类继承过来的虚表也是先拷贝一份基类的虚表,如果派生类中有虚函数重写,则用派生类中的虚函数代替基类中的虚函数,派生类中新增的虚函数构成一张虚表,如果派生类中没有新增虚函数,则不会有这张虚表,因此就不会有前四个字节去存放虚表的地址。如果派生类中既没有构造函数也没有析构函数,则不会有0x00000000这四个字节。简单的演示下这两种情况:

1)将派生类中新增的FunTest3()函数屏蔽,也即是派生类中没有新增的虚函数,再来看下内存:

2)将派生类中的构造函数屏蔽,放开刚刚屏蔽的FunTest3()函数。也就是派生类中既没有构造函数也没有析构函数,在来看下内存:


(2)多继承

注释:此代码中派生类中没有构造函数也没有析构函数,派生类中有自己新增的虚函数。

#include<iostream>
using  namespace std ;




class C1
{
public:
	virtual void Funtest1()
	{
		cout << "C1::Funtest1()" << endl;
	}
	virtual void Funtest2()
	{
		cout << "C1::Funtest2()" <<endl;
	}

public:
	int _data2;
};



class C2
{
public:
	virtual void Funtest3()
	{
		cout << "C2::Funtest1()" << endl;
	}
	virtual void Funtest4()
	{
		cout << "C2::Funtest3()" <<endl;
	}
public:
	int _data3;
};

class D:virtual public C1,virtual public C2
{
public:
	virtual void Funtest4()
	{
		cout << "D::Funtest3()" << endl;
	}
	virtual void Funtest2()
	{
		cout << "D::Funtest2()" <<endl;
	}

	virtual void Funtest7()   //派生类的虚函数。
	{
		cout << "D::Funtest7()" <<endl;
	}
	virtual void Funtest8()  //派生类的虚函数。
	{
		cout << "D::Funtest8()" <<endl;
	}
public:
	int _data4;
};

void test()
{
	cout << "sizeof(D):" << sizeof(D) << endl;  //sizeof(D):28   
	D d1;

	cout << "&d1" << &d1 <<endl;  //看下派生类的地址。&d10051F98C
	
	d1._data2 = 2;
	d1._data3 = 3;
	d1._data4 = 4;

	C1 &c1 = d1;

	cout << "&c1" << &c1 <<endl;  //看下基类C1去引用一个派生类,基类的地址。&c10051F998

	typedef void (*PVF)();
	PVF *pvf =  (PVF*)*(int*)&c1; //这就相当于一个二级指针指向一个指针数组。
	cout << "从C1继承过来的虚函数构成的虚表 :" << endl;

	while (*pvf)
	{
		(*pvf)();
		pvf++;
	}
	cout  << endl << endl;


	C2 &c2 = d1;  //c2的地址就是d1偏移C1类型的地址。
	cout << "&c2" << &c2 <<endl;  //看下基类C2去引用一个派生类,基类的地址。&c20051F9A0

	pvf =  (PVF*)*(int*)&c2; //这就相当于一个二级指针指向一个指针数组。

	cout << "从C2继承过来的虚函数构成的虚表:" << endl;
	while (*pvf)
	{
		(*pvf)();
		*pvf++;
	}

	cout  << endl;

	pvf =  (PVF*)*(int*)&d1; //这就相当于一个二级指针指向一个指针数组。

	cout << "派生类自己的虚函数构成的虚表:" << endl;
	while (*pvf)
	{
		(*pvf)();
		*pvf++;
	}

	cout  << endl;


}

int main()
{

	test();
	
	
	cout << "hello..." <<endl;
	system("pause");
	return 0;
}


说明:先拷贝基类的虚表,如果派生类中有虚函数重写,用重写的虚函数代替原来的虚函数,其他的不变,同样的如果派生类中有构造函数和析构函数之一或两者都有,则会多有四个字节存放0x00000000,如果派生类中没有新增虚函数,则不会有第一张虚表,因此也不会有前四个字节存放第一张虚表的地址。

(3)菱形继承

注释:此代码中派生类中没有构造函数也没有析构函数,派生类中有自己新增的虚函数。

#include<iostream>
using  namespace std ;

class B
{
public:
	virtual void Funtest1()
	{
		cout << "B::Funtest1()" << endl;
	}
public:
	int _data1;
};

class C1:virtual public B
{
public:
	virtual void Funtest1()
	{
		cout << "C1::Funtest1()" << endl;
	}
	virtual void Funtest2()
	{
		cout << "C1::Funtest2()" <<endl;
	}

public:
	int _data2;
};



class C2:virtual public B
{
public:
	virtual void Funtest1()
	{
		cout << "C2::Funtest1()" << endl;
	}
	virtual void Funtest3()
	{
		cout << "C2::Funtest3()" <<endl;
	}
public:
	int _data3;
};

class D:public C2,public C1
{
public:
	virtual void Funtest1()
	{
		cout << "D::Funtest1()" << endl;
	}
	virtual void Funtest3()
	{
		cout << "D::Funtest3()" << endl;
	}
	virtual void Funtest4()
	{
		cout << "D::Funtest4()" <<endl;
	}
public:
	int _data4;
};

void test()
{
	cout << "sizeof(D):" << sizeof(D) << endl;  //sizeof(D):28   c1 12+c2 12+d 4   = 28
	D d1;
	d1._data1 = 1;
	d1._data2 = 2;
	d1._data3 = 3;
	d1._data4 = 4;

	C1 &c1 = d1;

	typedef void (*PVF)();
	PVF *pvf =  (PVF*)*(int*)&c1; //这就相当于一个二级指针指向一个指针数组。
	cout << "从C1继承过来的虚函数构成的虚表:" << endl;

	while (*pvf)
	{
		(*pvf)();
		pvf++;
	}
	cout  << endl << endl;


	C2 &c2 = d1;
	pvf =  (PVF*)*(int*)&c2; //这就相当于一个二级指针指向一个指针数组。

	cout << "从C2继承过来的虚函数构成的虚表:" << endl;
	while (*pvf)
	{
		(*pvf)();
		*pvf++;
	}

	cout  << endl;

	B &b1 = d1;
	pvf =  (PVF*)*(int*)&b1; //这就相当于一个二级指针指向一个指针数组。

	cout << "从基类B继承的虚函数构成的虚表:" << endl;
	while (*pvf)
	{
		(*pvf)();
		*pvf++;
	}
	cout  << endl;
}





int main()
{

	test();

	cout << "hello..." <<endl;
	system("pause");
	return 0;
}






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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值