1. 程式人生 > >淺談多型中的虛擬函式和虛表

淺談多型中的虛擬函式和虛表

需要實現多型必不可少的就是虛擬函式,類的成員函式前加virtual關鍵字,這個成員函式就是虛擬函式;例如:

class T
{
	public:
	
	virtual void fun()
	{
		cout<<"fun()"<<endl;
	}
	
	int _t;

};
在不加virtual的情況下:sizeof(T)的大小為4;
加了vitual變成虛擬函式之後:sizeof(T)的大小為8;
這是為什麼呢??
   
存在虛擬函式的類的物件模型為:

由此圖可以看出T的物件中不僅有_t而且還有一個指標,這個指標指向的是一個存放虛擬函式地址的表,也就是虛表。所以在類成員函式前加了virtual之後的大小為8;
根據下圖就可以看出虛表中是怎麼去存放虛擬函式的地址:


在單繼承下的虛擬函式表又是什麼樣??

class T{
public:
	  virtual void fun1()
	{
		cout << "T::fun1()" << endl;
	}

	 virtual  void fun2()
	  {
		  cout << "T::fun1()" << endl;
	  }
	int _t;
};

class Z : public T{
public:
	virtual void fun1()
	{
		cout << "Z::fun1" << endl;
	}

	virtual void fun3()
	{
		cout << "Z::fun3" << endl;
	}
	int _z;
};
上述程式碼中,類Z單繼承T,重寫了T類中的fun1,fun2並沒有重寫,在Z類本身中還加了自己的虛擬函式fun3;
Z的物件模型為:

vs2013監視視窗:

由上圖監視視窗可以看出在__vfPtr所指的虛表中只有Z::fun1和T::fun2這兩個虛擬函式的地址,fun3並沒有存在於虛表中,這是因為vs2013下編譯器的一個bug,不過我們
換一種方式(列印虛表)來看虛表:

class T{
public:
	  virtual void fun1()
	{
		cout << "T::fun1()" << endl;
	}

	 virtual  void fun2()
	  {
		  cout << "T::fun1()" << endl;
	  }
	int _t;
};

class Z : public T{
public:
	virtual void fun1()
	{
		cout << "Z::fun1()" << endl;
	}

	virtual void fun3()
	{
		cout << "Z::fun3()" << endl;
	}
	int _z;
};

typedef void (*V_F) ();

void PrintVtable(int vptr)
{
	int * ptr = (int *)vptr;

	printf("虛表:0x%p\n",ptr);

	for (int i = 0; ptr[i] != 0; i++) //通常虛表以0為結束標誌;
	{
		V_F f = (V_F)ptr[i];
		f();
	}
}

void test()
{
	Z z;
	PrintVtable(*(int*)(&z));
}

執行截圖:

多繼承的虛擬函式表:

class T{
public:
	  virtual void fun1()
	{
		cout << "T::fun1()" << endl;
	}

	 virtual  void fun2()
	  {
		  cout << "T::fun2()" << endl;
	  }
	int _t;
};

class Z {
public:
	virtual void fun1()
	{
		cout << "Z::fun1()" << endl;
	}

	virtual void fun2()
	{
		cout << "Z::fun2()" << endl;
	}
	int _z;
};

class P : public Z,public T{
public:
	virtual void fun1()
	{
		cout << "P::fun1()" << endl;
	}

	virtual void fun3()
	{
		cout << "P::fun3()" << endl;
	}
};

typedef void (*V_F) ();

void PrintVtable(int vptr)
{
	int * ptr = (int *)vptr;

	printf("虛表:0x%p\n",ptr);

	for (int i = 0; ptr[i] != 0; i++) //通常虛表以0為結束標誌;
	{
		V_F f = (V_F)ptr[i];
		f();
	}
}

void test()
{
	
	P p;
	PrintVtable(*(int *)(&p));
}
執行截圖:

可以看出在虛擬函式表中沒有出現虛擬函式T::fun2();這是因為派生類中的虛擬函式會放在第一個繼承的虛表中;