天天看點

C語言實作C++多态思想前言:C++多态基礎:

前言:

接觸C++已經有一段時間了(工作時絕大部分時間使用的是C,因為從事的是嵌入式這方面,也用C#做Windows的軟體界面),感慨頗多,尤其是在文法方面,和C比較起來更自由和複雜,但是自由并不意味着就是不嚴謹,我覺得C++的嚴謹程度勝過C。 C是C++的子集,C++完全相容C的文法,而且,C++的思想,理論上,都可以用C語言進行實作,最近在學習Linux的核心源碼和U-boot源碼,裡面用C實作C++面向對象的思想非常非常多,而且寫的非常的精彩,我是沒想到原來C語言有這麼大的威力,靈活多變的用法也讓我這隻菜鳥甚為驚歎,彷佛打開了一個新世界O(∩_∩)O

C++多态基礎:

多态的地位

多态應該是面向對象的三大思想的集大成者吧。不僅需要封裝和繼承來作為多态的實作條件,還可以使用未來,把未來的接口都設計好,是以說多态是面向對象三大概念最為頂級的思想,遠遠超過的程式的代碼。多态是設計模式的基礎,多态是架構的基礎。

C++編寫多态

  • 要有繼承
  • 要有虛函數重寫(子類和父類裡要有同名函數,并用virtual聲明)
  • 用父類指針(父類引用)指向子類對象

    先用C++編寫一個最簡單的多态的程式來作為後面C語言編寫多态的參照

class A				//聲明一個父類
{
public:
	virtual void print(int a, int b, char* p)    			//虛函數
	{
		cout << "result=" << a + b << p << endl;
	}
};
class B : public A			//子類B繼承于父類A
{
public:
	virtual void print(int a, int b, char* p)			//虛函數
	{
		cout << "result=" << a * b << p << endl;
	}
};
class C :public A			//子類C繼承于父類A
{
public:
	virtual void print(int a, int b, char* p)      //虛函數
	{
		cout << "result=" << a - b << p << endl;
	}
};
class D :public A			//子類D繼承于父類A
{
public:
	virtual void print(int a, int b, char* p)
	{
		cout << "result=" << a / b << p << endl;		//虛函數
	}
};
void mainOP2(A* myp, int a, int b, char* p)      //架構
{
	myp->print(a, b, p);			
}
void main()
{
	A* ParentPT = NULL;  //建立一個父類指針
	A m0;
	B m1;
	C m2;
	D m3;
	mainOP2(&m0, 10, 5, "hello");
	mainOP2(&m1, 10, 5, "hello");
	mainOP2(&m2, 10, 5, "hello");
	mainOP2(&m3, 10, 5, "hello");
	system("pause");
}
           

B,C,D全部是A的派生類,void mainOP2(A* myp, int a, int b, char* p) 可以看作是一個程式的主架構,通過函數參數裡的父類指針myp來指向A,B,C,D,用一句myp->print(a, b, p); 代碼,就可以實作不同多個不同類的print(a, b, p);函數。那麼同樣的調用語句,有多種不同的表現形态。

main函數就是将不同的對象類型傳進mainOP2函數裡,mainOP2表現出不同表現形态。這應該是一個最淺顯簡單的多态表現形式了,應該大家都能看得懂。

C++類中的虛函數表

當在類中有virtual聲明虛函數的時候,編譯器會在每個類中生成一個虛函數表(偷偷的生成),隻有被聲明為virtual才有資格進入虛函數表。虛函數表本質是一個存儲類成員函數指針的資料結構。虛函數表是由編譯器自動生成與維護的。

然後,在建立一個類的執行個體化對象的時候,例如 A m0; 如果這個類A裡邊存在虛函數,那麼編譯器會偷偷的建立一個指向類A的虛函數表的指針,就是傳說中的vptr指針,這個指針是确确實實存在的,不信可以sizeof(A)看一下是多少,因為類A裡面沒有任何一個成員屬性,是以理論上編譯器為了讓對象有一個獨一無二的記憶體空間,會給類A配置設定1個位元組,但是實際上列印sizeof(A)是4個位元組,因為編譯器偷偷的建立了一個vptr指針。

void mainOP2(A* myp, int a, int b, char* p)函數在執行的時候,編譯器會判斷函數體裡面的執行語句print(a, b, p);是不是虛函數,如果不是虛函數,編譯器可直接确定被調用的成員函數,屬于靜态聯編。

如果是虛函數,編譯器根據傳進來的實參的vptr指針,所指的虛函數表去查找print()函數,并調用,屬于動态聯編。因為不管你傳進來是父類的變量還是子類的變量,都

有一個vptr指針,這樣就可以根據具體對象調用具體的函數。

多态屌就屌在這裡,在你建立類的執行個體化對象的時候,就已經提前布局了,給你偷偷多了一個vptr指針的機制。

在下面的C語言實作多态的過程中,C語言也會有相同的一種機制來映射這個虛函數表。

用C語言實作C++多态

前面的一堆鋪設,終于講到重點了,d=====( ̄▽ ̄*)b

先以單個函數為例:

typedef void(*FuncPT)(int a, int b, char* p);
void print1(int a, int b,char* p)
{
	cout << "result=" << a + b << p << endl;
	/* 理論上來說,用純粹的C語言實作,這裡應該是用printf()函數,但是主要是為了和C++作對比,是以
	 * 就先不糾結這個了。在CPP檔案下,聲明完命名空間和頭檔案,用C語言的思維編寫代碼,這樣寫
	 * 也是完全沒問題的。
	 */
}

void mainOP(FuncPT myp, int a, int b, char* p)
{
	(*myp)(a, b, p);
}

void main()
{
	mainOP(print1, 10, 5, "hello");
	system("pause");
}
           

聲明一個函數指針類型FuncPT,以函數指針作為函數參數,編寫一個函數

mainOP(FuncPT myp, int a, int b, char* p),調用mainOP()的時候,把要調用的函數指針作為實參傳遞進去,mainOP(print1, 10, 5, “hello”);,就可以成功調用print1(int a, int b,char* p),這樣,經過mainOP()函數間接調用print1()函數,如果是單個函數調用,這樣看似很蠢,但是這就是多态的原型。

然後更新一下,換成很多個函數:

typedef void(*FuncPT)(int a, int b, char* p);

void print1(int a, int b,char* p)
{
	cout << "result=" << a + b << p << endl;
}
void print2(int a, int b, char* p)
{
	cout << "result=" << a * b << p << endl;
}
void print3(int a, int b, char* p)
{
	cout << "result=" << a - b << p << endl;
}
void print4(int a, int b, char* p)
{
	cout << "result=" << a / b << p << endl;
}

void mainOP(FuncPT myp, int a, int b, char* p)
{
	(*myp)(a, b, p);
}

void main()
{
	mainOP(print1, 10, 5, "hello");
	mainOP(print2, 10, 5, "hello");
	mainOP(print3, 10, 5, "hello");
	mainOP(print4, 10, 5, "hello");
	system("pause");
}
           

增加3個print()函數,一字不動,不更改架構mainOP情況下,main()函數也可以通過mainOP調用新增的幾個函數。這不就是多态嗎,這跟多态的表現形式是完全一樣的。主架構mainOP()函數不變,外面的子函數隻要按照typedef void(FuncPT)(int a, int b, char p);提供的函數類型編寫函數,不論以後增加多少個同類型的函數,還是減少,mainOP都可以原封不動,直接耦合。

和C++的關系映射:

mainOP()=== >主架構

typedef void(FuncPT)(int a, int b, char p); = =>父類虛函數或者純虛函數,提前布局

print1(), print2(),print3(), print4()===>表示4個有繼承關系的類裡邊函數原型相同的四個虛函數。

print1(), print2(),print3(), print4()的函數指針= =>每個函數的指針就是它所在的那個類裡邊虛函數表中的其中一個。

牛逼啊!!!!!!!!!!

工作中,因為做嵌入式工作,主要還是以C為主,就算是做Windows的圖形界面的軟體,也是首選C#,開發效率很高。實在是被C++的魅力深深吸引了,蹩腳複雜的文法下,了解透了,往往又是給自己打開了一扇新世界的大門。也嘗試過用MFC來做圖形界面,但是很快就放棄了,實在是太醜了!而且開發效率也不高。希望自己,不忘初心,能多學習一點。

以上很多知識都是查找資料,外加自己的了解總結而成的,肯定存在些許錯誤,如果哪位大神有發現錯誤的地方,請及時指正。

——冷亦花煙_CYB(菜蔡)

2019.2.25 23:28

繼續閱讀