您的位置:首页 > 编程语言 > C语言/C++

c++ 多态/虚表剖析

2016-11-13 10:45 399 查看
【[b]对象的类型】[/b]



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

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

【多态】



多态:一词最初来源于希腊语,意思是具有多种形式或形态的情形,在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函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: