在Linux的核心代碼中,多處都用到物件導向的設計理念,比如,在檔案系統系統的設計中,有四個主要的對象:超級塊,索引節點,目錄項和檔案。在每個對象定義的結構體,都有一個指向指向對象操作的結構體,在這個結構體中的所有欄位都是指向函數的指標,例如file結構中的縮減定義:
struct file
{
struct file_operations *f_op; /*指向檔案動作表的指標*/
mode_t f_mode; /*檔案的開啟模式*/
loff_t f_pos; /*檔案的當前位置*/
…
};
對檔案進行操作的一組函數叫檔案動作表,由file_operations結構描述:
struct file_operations {
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
…
};
一直以為這樣的定義是理所當然的,直到有一天,有同學問起為什麼這樣定義,源頭在哪裡?只是告訴學生去理解物件導向的思想,然後再看這裡的資料結構就容易理解了。而實際上,物件導向的思想如果用結構化語言來表達,其理解難度上還是有一個坡度的。恰好在IBM developerworks網站看到“技巧:用 C 語言實現程式的多態性”一文,以形象化的語言說明了這種定義的淵源:
前言:關於多態,關於 C
多態 (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 檔案裡定義:
具體內容參看:http://www.ibm.com/developerworks/cn/linux/l-cn-cpolym/
-----------------------------------------------------------------------------
後記:本文給出的這樣一個思維過程,對於把物件導向的思想與結構化語言如何結合起來,無疑起一個嚮導作用。 儘管本文給出了用C語言實現鏈表的多態性。但是核心代碼list.h中對鏈表的經典實現(參看我以前的博文),並沒有以多態的形式,但其抽象性和靈活應用的價值,在分析並實踐後就能體現出來。