程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++繼承、虛函數處的面試題

C++繼承、虛函數處的面試題

編輯:關於C++

昨天,收到SenseTime公司面試官的電話面試(一天面了三家公司,收獲挺多的),通話時間將近1個半小時,面試過程中暴露出很多知識上的漏洞,本篇文章針對面試過程中繼承以及虛函數方面的知識做一總結,查缺補漏,希望對大家有幫助。

單繼承下的虛函數表

 

//單繼承下虛函數表:是如何組織的
class A{
public:
	virtual void func(){
		cout << "A::func" << endl;
	}
	virtual void funcA(){
		cout << "A::funcA" << endl;
	}
};

class B:public A{
public:
	virtual void func(){
		cout << "B::func" << endl;
	}
	virtual void funcB(){
		cout << "B::funcB" << endl;
	}
};


class C:public A{
public:
	virtual void func(){
		cout << "C::func" << endl;
	}
	virtual void funcC(){
		cout << "C::funcC" << endl;
	}
};

typedef void (*FUNC)();
int main()
{
	A a;
	B b;
	C c;
	cout << "A::虛表:" << endl;
	((FUNC)(*(int *)(*(int*)(&a))))();
	((FUNC)(*((int*)(*(int*)(&a)) + 1)))();
	cout << "-------------------------------------" << endl;

	cout << "B::虛表:" << endl;
	((FUNC)(*(int *)(*(int*)(&b))))();
	((FUNC)(*((int*)(*(int*)(&b)) + 1)))();
	((FUNC)(*((int*)(*(int*)(&b)) + 2)))();
	cout << "-------------------------------------" << endl;

	cout << "C::虛表:" << endl;
	((FUNC)(*(int *)(*(int*)(&c))))();
	((FUNC)(*((int*)(*(int*)(&c)) + 1)))();
	((FUNC)(*((int*)(*(int*)(&c)) + 2)))();
	system("pause");
	return 0;
}
\

 

 

問題1:三個類中都有虛函數,所以三個類均有各自獨立的虛函數表

問題2:三個虛函數表的組織情況,如上圖所示

問題3:B、C各自擁有自己的虛函數表,互不影響

問題4:類的對象只存儲指向虛函數表的指針vfptr(一般存儲在對象內存布局的最前面),虛函數表只有一份,為所有對象所共享,vtable在Linux/Unix中存放在可執行文件的只讀數據段中(rodata),而微軟的編譯器將虛函數表存放在常量段點擊打開鏈接

 

typedef void(*FUNC)();
class A{
public:
	virtual void func(){
		cout << "A::func" << endl;
	}
	virtual void funcA(){
		cout << "A::funcA" << endl;
	}
private:
	int a;
};
class B{
public:
	virtual void func(){
		cout << "B::func" << endl;
	}
	virtual void funcB(){
		cout << "B::funcB" << endl;
	}
private:
	int b;
};
class C :public A, public B{
public:
	virtual void func(){
		cout << "C::func" << endl;
	}
	virtual void funcC(){
		cout << "C::funcC" << endl;
	}
private:
	int c;
};typedef void(*FUNC)();
class A{
public:
	virtual void func(){
		cout << "A::func" << endl;
	}
	virtual void funcA(){
		cout << "A::funcA" << endl;
	}
private:
	int a;
};
class B{
public:
	virtual void func(){
		cout << "B::func" << endl;
	}
	virtual void funcB(){
		cout << "B::funcB" << endl;
	}
private:
	int b;
};
class C :public A, public B{
public:
	virtual void func(){
		cout << "C::func" << endl;
	}
	virtual void funcC(){
		cout << "C::funcC" << endl;
	}
private:
	int c;
};

 

 

多繼承條件下的虛函數表

//多繼承條件下的虛函數表
void test()
{
	C c;
	cout << "多繼承條件下的虛函數表:" << endl;
	cout << "------------------------" << endl;
	((FUNC)(*((int*)(*(int *)(&c)))))();
	((FUNC)(*((int*)(*(int*)(&c)) + 1)))();
	((FUNC)(*((int*)(*(int*)(&c)) + 2)))();
	cout << "------------------------" << endl;
	((FUNC)(*(int*)(*((int*)(&c) + 2))))();
	((FUNC)(*((int*)(*((int*)(&c) + 2)) + 1)))();
}


\

對象C的內存空間分布如下圖所示:

\

多繼承條件下,基類指針指向派生類後,基類指針所能訪問的函數

 

//多繼承條件下,基類指針指向派生類後,基類指針所能訪問的函數
void test1()
{
	C c;
	A *pa = &c;
	B *pb = &c;
	C *pc = &c;
	cout << "基類指針pa所能調用的函數:" << endl;
	pa->func();
	pa->funcA();
	//pa->funcB();error:提示類A沒有成員funcB、funcC  -->受到類型的限制
	//pa->funcC();error


	cout << "基類指針pb所能調用的函數:" << endl;
	pb->func();
	pb->funcB();
	//pb->funcA();error
	//pb->funcC();error

	cout << "派生類指針pc所能調用的函數:" << endl;
	pc->func();
	pc->funcA();
	pc->funcB();
	pc->funcC();
}
\

 

問題1:pa和pc的值相同,都是對象c的首地址,pb和pa至今相差四個字節(int a造成的-->觀察上圖的內存空間分配)

問題2:基類指針指向派生類的對象,通過該基類指針所能訪問的函數受類型的限制(運行時調用哪個函數受多態的影響)

\

3:由於多態,會訪問C類的func

4:通過加作用域:pa->A::func()

 

多繼承條件下,基類指針指向派生類對象後,基類指針之間強制類型轉化之後,所能訪問的函數

 

void test2()
{
	C c;
	A *pa = &c;
	B *pb = &c;
	C *pc = &c;

	pa = reinterpret_cast(pb);
	pa->func();
	pa->funcA();      

	//pb = reinterpret_cast(pa);
	//pb->func();
	//pb->funcB();

	//pa = reinterpret_cast(pc);
	//pa->func();
	//pa->funcA();

}
\

 

結果很奇怪(查看匯編分析)

\

 

最後一個簡單地問題如下

\

相信你經過以上問題的分析,一定會回答出來(答案已經在圖裡面了^_^)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved