用C語言實現程式的多型性

2021-06-03 10:31:51 字數 4328 閱讀 7160

多型 (polymorphism) 一詞最初**於希臘語 polumorphos,含義是具有多種形式或形態的情形。在程式設計領域,乙個廣泛認可的定義是「一種將不同的特殊行為和單個泛化記號相關聯的能力」。然而在人們的直觀感覺中,多型的含義大約等同於「同乙個方法對於不同型別的輸入引數均能做出正確的處理過程,並給出人們所期望獲得的結果」,也許這正體現了人們對於多型性所能達到的效果所寄予的期望:使程式能夠做到越來越智慧型化,越來越易於使用,越來越能夠使設計者透過形形色色的表象看到**所要觸及到的問題本質。

作為讀者的你或許對於物件導向程式設計已有著精深的見解,或許對於多型的方便與神奇你也有了深入的認識。這時候你訝異的開始質疑了:「多型,那是物件導向程式設計才有的技術,c 語言是面向過程的啊!」而我想說的是,c 語言作為一種程式語言,也許並不是為了物件導向程式設計而設計,但這並不意味著它不能實現物件導向程式設計所能實現的功能,就比如說,多型性。

在本文中我們使用乙個簡單的單鏈表作為例子,展示 c 語言是如何體現多型性的。

結構體:不得不說的故事

許多從寫 c **開始,逐漸走向 c++ 的程式設計師都知道,其實 c++ 裡面的 class,其前身正是 c 語言中的 structure。很多基於 c 語言背景介紹 c++ 的書籍,在介紹到 class 這一章的時候都會向讀者清晰地展示,乙個 c 語言裡的 structure 是怎樣逐漸變成乙個典型的 c++ class 的,甚至最後得出結論:「structure 就是乙個所有成員都公有的類」,當然了,class 還是 class,不能簡單的把它看做乙個複雜化了的 structure 而已。

下面我們來看看在 c 語言中定義乙個簡單的儲存整型資料的單鏈表節點是怎麼做的,當然是用結構體。大部分人會像我一樣,在 linklist.h 檔案裡定義:

雙擊**全選

typedef struct node* linklist;

struct node                    // 鍊錶節點

;鍊錶有了,下面就是你想要實現的一些鍊錶的功能,當然是定義成函式。我們只舉幾個常用功能:

雙擊**全選

inklist initiallinklist();                // 初始化鍊錶

link newlinklist (int data);            // 建立新節點

void insertfirst(linklist h,int data);       // 在已有鍊錶的表頭進行插入節點操作

void linklistoutput(linklist h);              // 輸出鍊錶中資料到控制台

這些都是再自然不過的 c 語言的程式設計過程,然後我們就可以在 linklist.c 檔案中實現上述兩個函式,繼而在 main.c 中呼叫它們了。

然而上面我們定義的鍊錶還只能對整型資料進行操作。如果下次你要用到乙個儲存字串型別的鍊錶,就只好把上面的過程重新來過。也許你覺得這個在原有**基礎上做略微修改的過程並不複雜,可是也許我們會不斷的增加對於鍊錶這個資料結構的操作,而需要用鍊錶來儲存的資料型別也越來越多,這些都意味著海量的**和繁瑣的後期維護工作。當你有了上百個儲存不同資料型別的鍊錶結構,每當你要增加乙個操作,或者修改某個操作的傳入引數,工作量會變大到像一場災難。

但是我們可以改造上述**,讓它能夠處理你所想要讓它處理的任何資料型別:實行,字元型,乃至任何你自己定義的 structure 型別。

void*:萬能的指標「掛鉤」

幾乎所有講授 c 語言課程的老師都會告訴你:「指標是整個 c 語言的精髓所在。」而你也一直敬畏著指標,又愛又恨地使用著它。許多教材都告訴你,int * 叫做指向整型的指標,而 char * 是指向字元型的指標,等等不一而足。然而這裡有乙個另類的指標家族成員—— void *。不要按照通常的命名方式叫它做指向 void 型別的指標,它的正式的名字叫做:可以指向任意型別的指標。你一定注意到了「任意型別」這四個字,沒錯,實現多型,我們靠的就是它。

下面來改造我們的鍊錶**,在 linklist.h 裡,如下:

雙擊**全選

typedef struct node* linklist;

struct node                    // 鍊錶節點

;linklist initiallinklist();               // 初始化鍊錶

link newlinklist (void *data);          // 建立新節點

void insertfirst(linklist h, void *data);     // 在已有鍊錶的表頭進行插入節點操作

void linklistoutput(linklist h);             // 輸出鍊錶中資料到控制台

我們來看看現在這個鍊錶和剛才那個只能儲存整型資料的鍊錶的區別。

當你把 node 結構體裡面的成員定義為乙個整型資料,就好像把這個鍊錶節點打造成了乙個大小形狀固定的盒子,你定義乙個鍊錶節點,程式進行編譯的時候編譯器就為你打造乙個這樣的盒子:裝乙個 int 型別的資料,然後裝乙個 linklist 型別的指標。如果你想強行在這個盒子裡裝別的東西,編譯器會告訴你,對不起,盒子的大小形狀並不合適。所以你必須為了裝各種各樣型別的資料打造出不同的生產盒子的流水線,想要裝哪種型別資料的盒子,就開啟對應的流水線來生產。

但是當你把結構體成員定義為 void *,一切都變得不同了。這時的鍊錶節點不再像個大小形狀固定的盒子,而更像乙個掛鉤,它可以掛上乙個任意型別的資料。不管你需要儲存什麼型別的資料,你只要傳遞乙個指標,把它儲存到 node 節點中去,就相當於把這個資料「掛」了上去,無論何時你都可以根據指標找到它。這時的鍊錶彷彿變成了一排貼上在牆上的衣帽鉤,你可以掛一排大衣,可以掛一排帽子,可以掛一排圍巾,甚至,你可以併排掛一件大衣一頂帽子一條圍巾在牆上。void * 初露猙獰,多型離 c 語言並不遙遠。

實現:你的多型你做主

當你真正開始著手做這個工作的時候,你會發現把資料放入鍊錶中的操作和普通的存放 int 型別的鍊錶的實現並沒有什麼大的區別,很方便。但是當你要把已經存進去的資料讀取出來的時候,就有一點麻煩了。對於 void * 型別的指標,編譯器只知道它裡面儲存了乙個位址,但是關於這個位址裡的資料型別,編譯器是沒有任何概念的。畢竟我們不能指望編譯器什麼都知道,什麼都能替你做好,所以存進去的資料的型別,作為程式設計師的我們必須清楚的知道,並且在取出這個資料的時候,用這一型別的指標來對 void * 做強制型別轉換。

為了方便的做到這一點,我採取的方法是在 node 結構體中增加乙個標識資料型別的域,並用乙個列舉型別來存放這些資料型別。這時的 linklist.h 如下所示:

雙擊**全選

#ifndef linklist_h

#define linklist_h

typedef struct node* linklist;

enum datatype

;struct node                        // 鍊錶節點

;linklist initiallinklist();                // 初始化鍊錶

linklist newlinklist (void *data, int datatype);     // 建立新節點

void insertfirst(linklist h, void *data, int datatype);  // 在已有鍊錶的表頭進行插入節點操作

void linklistoutput(linklist h);             // 輸出鍊錶中資料到控制台

#endif

初始化鍊錶,**如下:

雙擊**全選

linklist initiallinklist()

建立新節點,**如下:

雙擊**全選

linklist newlinklist (void *data, int datatype)

在已有鍊錶的表頭進行插入節點操作,**如下:

雙擊**全選 

void insertfirst(linklist h, void *data, int datatype)

輸出鍊錶中資料到控制台,**如下:

雙擊**全選

void linklistoutput(linklist h)

case 1:

case 2:

case 3:

}p = p->next;

}printf("\n");}小結

通過上面這個鍊錶的小例子,大家可能已經看到了c 語言的靈活性。這段**雖然短並且功能簡單,但是已經實現了多型性。這篇文章的本意並不在於想要告訴大家用c 實現多型的方法,而多型的含義也無疑是更加廣泛的。這篇文章的初衷其實是基於這樣一點認識:物件導向是一種程式設計思想,而 c 語言則是一種程式語言。也許它並不是專門為了物件導向程式設計而設計,但是這絕不意味著它不能實現物件導向的程式設計。當然以上所展示的這幾個操作,如果是用別的程式語言,可能只要寥寥幾行就可以完成,但是 c 語言想告訴我們的是:也許我不擅長,但是並不意味著我做不到。

C 中多型性的實現

c 語言三大特性 繼承,封裝,多型 一 多型性 多型是指乙個行為具有多個不同表現形式的能力,在c 中通過多型性的檢測時機可以分為靜態多型性和動態多型性 靜態多型性 函式過載和運算子過載 動態多型性 抽象方法 重寫方法 隱藏方法 二 函式過載 overlode 函式名必須相同 函式引數型別不同 函式引...

C 的多型性

c 的多型性 1.多型性的概念 多型性是指用乙個名字定義不同的函式,這函式執行不同但又類似的操作,從而實現 乙個介面,多種方法 多型性的實現與靜態聯編 動態聯編有關。靜態聯編支援的多型性稱為編譯時的多型性,也稱靜態多型性,它是通過函式過載和運算子過載實現的。動態聯編支援的多型性稱為執行時的多型性,也...

C 的多型性

type text css rel stylesheet href 是允許將父物件設定成為和乙個或多個它的子物件相等的技術,比如parent child 多型性使得能夠利用同一類 基類 型別的指標來引用不同類的物件,以及根據所引用物件的不同,以不同的方式執行相同的操作.c 中多型更容易理解的概念為允...