c++ 多态/虚表剖析

对象的类型


静态类型:对象声明时的类型,是在编译时确定的

动态类型:目前所指对象的类型,是在运行时确定的

【多态】


多态:一词最初来源于希腊语,意思是具有多种形式或形态的情形,在c++语言中多态有着更广泛的含义

静态多态:编译器在编译期间完成的,编译器根据函数实参的类型可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数有调用该函数,否则出现编译错误。

【动态多态】

多态绑定:在程序执行期间(非编译器)判断所引用对象的实际类型,根据其实际类型调用相应的方法。

使用virtual关键字修饰类的成员函数时, 指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。

class CWashRoom
{
public:
	void GoToManWashRoom()
	{
		cout << "Man--->Please Left" << endl;
	}
	void GoToWomanWashRoom()
	{
		cout << "Woman--->Please Right" << endl;
	}
};
class CPerson
{
public:
	virtual void GoToWashRoom(CWashRoom & _washRoom) = 0;
};
class CMan :public CPerson
{
public:
	virtual void GoToWashRoom(CWashRoom & _washRoom)
	{
		_washRoom.GoToManWashRoom();
	}
};
class CWoman :public CPerson
{
public:
	virtual void GoToWashRoom(CWashRoom & _washRoom)
	{
		_washRoom.GoToWomanWashRoom();
	}
};
void FunTest()
{
	CWashRoom washRoom;
	for (int iIdx = 1; iIdx <= 10; ++iIdx)
	{
		CPerson* pPerson;
		int iPerson = rand() % iIdx;
		if (iPerson & 0x01)
		{
			pPerson = new CMan;
		}
		else
		{
			pPerson = new CWoman;
		}
		pPerson->GoToWashRoom(washRoom);
		delete pPerson;
		pPerson = NULL;
		Sleep(1000);
	}
}

【动态绑定条件】

1.必须是虚函数

2.通过基类类型的引用或者指针调用虚函数

继承体系同名成员函数的关系

1.重载:

             在同一作用域;

             函数名相同、参数不同;

             返回值可以不同。

2.重写(覆盖):

                              不在同一作用域(分别在基类和派生类);

                              函数名相同/参数相同/返回值相同(协变(基类返回基类指针,派生类返回派生类指针)例外);

                              基类函数必须有virtual关键字;

                              访问修饰符可以不同。

3.重定义(隐藏):

                                  在不同作用域中(分别在基类和派生类);

                                  函数名相同;

                                  在基类和派生类中只要不构成重写就是重定义。

【纯虚函数】

在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。

virtual void Display () = 0; // 纯虚函数

1.派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)

2.基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。

3.只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数。

4.如果在类外定义虚函数,只能在声明函数时附加virtual关键字,定义时不用加。

5.构造函数不能定义为虚函数,虽然可以将operator=定义为虚函数,但最好不要这么做,使用时容易混淆。

6.不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象时不完整的,可能会出现未定义的行为。

7.最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)

8.虚表是所有类对象实例共用的。

虚表剖析

class CTest
{
public:
	CTest()
	{
		iTest = 10;
		cout << "this =" << this << endl;
	}
	virtual ~CTest(){};
private:
	int iTest;
};
int main()
{
	{
		CTest test;
		cout << sizeof(test) << endl;
	}
}


对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针。

【没有覆盖】


class CBase
{
public:
	CBase(){ m_iTest = 10; }
	virtual void FunTest0(){ cout << "CBase::FunTest0()"; }
	virtual void FunTest1(){ cout << "CBase::FunTest1()"; }
	virtual void FunTest2(){ cout << "CBase::FunTest2()"; }
private:
	int m_iTest;
};
class CDerived :public CBase
{
public:
	virtual void FunTest4(){ cout << "CDerived::FunTest4()"; }
	virtual void FunTest5(){ cout << "CDerived::FunTest5()"; }
	virtual void FunTest6(){ cout << "CDerived::FunTest6()"; }
};
typedef void(*FUN_TEST)();
void FunTest()
{
	CBase base;
	cout << "CBase vfptr:" << endl;
	for (int iIdx = 0; iIdx < 3; ++iIdx)
	{
		FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&base + iIdx));
		funTest();
		cout << ": " << (int *)funTest << endl;
	}
	cout << endl;
	CDerived derived;
	cout << "CDerived vfptr:" << endl;
	for (int iIdx = 0; iIdx < 6; ++iIdx)
	{
		FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&derived + iIdx));
		funTest();
		cout << ": " << (int *)funTest << endl;
	}
}
int main()
{
	FunTest();
	return 0;
}
说明:

1、虚函数按照其声明顺序存在于虚表中

2、在派生类中,前面是基类的虚函数,后面是派生类中虚函数

【有覆盖】

class CBase
{
public:
	virtual void FunTest0(){ cout << "CBase::FunTest0()" << endl; }
	virtual void FunTest1(){ cout << "CBase::FunTest1()" << endl; }
	virtual void FunTest2(){ cout << "CBase::FunTest2()" << endl; }
	virtual void FunTest3(){ cout << "CBase::FunTest3()" << endl; }
};
class CDerived :public CBase
{
public:
	virtual void FunTest0(){ cout << "CDerived::FunTest0()" << endl; }
	virtual void FunTest1(){ cout << "CDerived::FunTest1()" << endl; }
	virtual void FunTest4(){ cout << "CDerived::FunTest4()" << endl; }
	virtual void FunTest5(){ cout << "CDerived::FunTest5()" << endl; }
};
typedef void(*_pFunTest)();
void FunTest()
{
	CBase base;
	for (int iIdx = 0; iIdx < 4; ++iIdx)
	{
		_pFunTest pFunTest = (_pFunTest)(*((int*)*(int *)&base + iIdx));
		pFunTest();
	}
	cout << endl;
	CDerived derived;
	for (int iIdx = 0; iIdx < 6; ++iIdx)
	{
		_pFunTest pFunTest = (_pFunTest)(*((int*)*(int *)&derived + iIdx));
		pFunTest();
	}
}
void TestVirtual()
{
	CBase base0;
	CDerived derived;
	CBase& base1 = derived;
}
int main()
{
	FunTest();
	TestVirtual();
	return 0;
}




【多重继承:没有虚函数覆盖】


class CBase0
{
public:
	CBase0(){ m_iTest = 0xB0; }
	virtual void PrintB0(){ cout << "m_iTest = " << hex << m_iTest << " CBase0::PrintB0()" << endl; }
	int m_iTest;
};
class CBase1
{
public:
	CBase1(){ m_iTest = 0xB1; }
	virtual void PrintB1(){ cout << "m_iTest = " << hex << m_iTest << " CBase1::PrintB1()" << endl; }
	int m_iTest;
};
class CBase2
{
public:CBase2(){ m_iTest = 0xB2; }
	   virtual void PrintB2(){ cout << "m_iTest = " << hex << m_iTest << " CBase2::PrintB2()" << endl; }
	   int m_iTest;
};
class CDerived :public CBase0, public CBase1, public CBase2
{
public:
	CDerived(){ m_iTest = 0xD0; }
	virtual void PrintD(){ cout << "m_iTest = " << hex << m_iTest << " CDerived::PrintD()" << endl; }
	int m_iTest;
};
typedef void(*VFTABLE_FUN)();
void PrintVfPTab(char * _pStr, int *_pVfAddr)
{
	cout << _pStr << endl;
	for (int iIdx = 0;; iIdx++)
	{
		VFTABLE_FUN pPrintVTab = (VFTABLE_FUN)(*(_pVfAddr + iIdx));
		if (NULL == pPrintVTab)
		{
			break;
		}
		pPrintVTab();
		cout << (int*)pPrintVTab << endl;
	}
	cout << endl;
}
void FunTest()
{
	CDerived derived;
	cout << sizeof(derived) << endl;
	int *pVfTAddr = NULL;
	CBase0& base0 = derived;
	pVfTAddr = (int*)(*(int *)&base0);
	PrintVfPTab("CBase0 virtual Tab:", pVfTAddr);
	CBase1& base1 = derived;
	pVfTAddr = (int*)(*(int *)&base1);
	PrintVfPTab("CBase1 virtual Tab:", pVfTAddr);
	CBase2& base2 = derived;
	pVfTAddr = (int*)(*(int *)&base2);
	PrintVfPTab("CBase2 virtual Tab:", pVfTAddr);
	pVfTAddr = (int*)(*(int *)&derived);
	PrintVfPTab("CDerived virtual Tab:", pVfTAddr);
	derived.PrintB0();
	derived.PrintB1();
	derived.PrintB2();
	derived.PrintD();
}
int main()
{
	FunTest();
	return 0;
}

【多重继承:有虚函数覆盖】


class CBase0
{
public:
	CBase0(){ m_iTest = 0xA0; }
	virtual void Print(){ cout << "m_iTest = " << hex << m_iTest << " CBase2::Print()" << endl; }
	int m_iTest;
};
class CBase1
{
public:
	CBase1(){ m_iTest = 0xB0; }
	virtual void Print(){ cout << "m_iTest = " << hex << m_iTest << " CBase2::Print()" << endl; }
	int m_iTest;
};
class CBase2{
public:
	CBase2(){ m_iTest = 0xC0; }
	virtual void Print(){ cout << "m_iTest = " << hex << m_iTest << " CBase2::Print()" << endl; }
	int m_iTest;
};
class CDerived :public CBase0, public CBase1, public CBase2
{
public:
	CDerived(){ m_iTest = 0xD0; }
	virtual void Print(){ cout << "m_iTest = " << hex << m_iTest << " CDerived::Print()" << endl; }
	int m_iTest;
};
void FunTest()
{
	CDerived derived;
	cout << sizeof(derived) << endl;
	CBase0& base0 = derived;
	base0.Print();
	CBase1& base1 = derived;
	base1.Print();
	CBase2& base2 = derived;
	base2.Print();
	derived.Print();
}
int main()
{
	FunTest();
	return 0;
}

因为Print()为虚函数,CDerived的虚表中覆盖了基类的虚函数,所以每次调用都调用的是CDeried中的print函数。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值