日本亚洲欧美日韩中文字幕_精品欧美一区二区三区久久久_久久av高潮av无码av_成在人线av无码免费_亚洲中文字幕无码久久精品1

15318988340

全國統(tǒng)一學(xué)習(xí)專線 8:30-21:00

前言:關(guān)于多態(tài),關(guān)于 C多態(tài) (polymorphism) 一詞最初來源于希臘語 polumorphos,含義是具有多種形式或形態(tài)的情形。在程序設(shè)計(jì)領(lǐng)域,一個廣泛認(rèn)可的定義是“一種將不同的特殊行為和單個泛化記號相關(guān)聯(lián)的能力”。然而在人們的直觀感覺中,多態(tài)的含義大約等同于“同一個方法對于不同類型的輸入?yún)?shù)均能做出正確的處理過程,并給出人們所期望獲得的結(jié)果”,也許這正體現(xiàn)了人們對于多態(tài)性所能達(dá)到的效果所寄予的期望:使程序能夠做到越來越智能化,越來越易于使用,越來越能夠使設(shè)計(jì)者透過形形色色的表象看到代碼所要觸及到的問題本質(zhì)。

作為讀者的你或許對于面向?qū)ο缶幊桃延兄畹囊娊?,或許對于多態(tài)的方便與神奇你也有了深入的認(rèn)識。這時候你訝異的開始質(zhì)疑了:“多態(tài),那是面向?qū)ο缶幊滩庞械募夹g(shù),C 語言是面向過程的啊!”而我想說的是,C 語言作為一種編程語言,也許并不是為了面向?qū)ο缶幊潭O(shè)計(jì),但這并不意味著它不能實(shí)現(xiàn)面向?qū)ο缶幊趟軐?shí)現(xiàn)的功能,就比如說,多態(tài)性。

在本文中我們使用一個簡單的單鏈表作為例子,展示 C 語言是如何體現(xiàn)多態(tài)性的。

結(jié)構(gòu)體:不得不說的故事

許多從寫 C 代碼開始,逐漸走向 C++ 的程序員都知道,其實(shí) C++ 里面的 class,其前身正是 C 語言中的 structure。很多基于 C 語言背景介紹 C++ 的書籍,在介紹到 class 這一章的時候都會向讀者清晰地展示,一個 C 語言里的 structure 是怎樣逐漸變成一個典型的 C++ class 的,甚至最后得出結(jié)論:“structure 就是一個所有成員都公有的類”,當(dāng)然了,class 還是 class,不能簡單的把它看做一個復(fù)雜化了的 structure 而已。

下面我們來看看在 C 語言中定義一個簡單的存儲整型數(shù)據(jù)的單鏈表節(jié)點(diǎn)是怎么做的,當(dāng)然是用結(jié)構(gòu)體。大部分人會像我一樣,在 linkList.h 文件里定義:

typedef struct Node* linkList;

struct Node                    // 鏈表節(jié)點(diǎn)

{

int data;                 // 存儲的整型數(shù)據(jù)

linkList next;              // 指向下一個鏈表節(jié)點(diǎn)

};

鏈表有了,下面就是你想要實(shí)現(xiàn)的一些鏈表的功能,當(dāng)然是定義成函數(shù)。我們只舉幾個常用功能:

linkList initialLinklist();                // 初始化鏈表

link newLinkList (int data);            // 建立新節(jié)點(diǎn)

void insertFirst(linkList h,int data);       // 在已有鏈表的表頭進(jìn)行插入節(jié)點(diǎn)操作

void linkListOutput(linkList h);              // 輸出鏈表中數(shù)據(jù)到控制臺

這些都是再自然不過的 C 語言的編程過程,然后我們就可以在 linkList.c 文件中實(shí)現(xiàn)上述兩個函數(shù),繼而在 main.c 中調(diào)用它們了。

然而上面我們定義的鏈表還只能對整型數(shù)據(jù)進(jìn)行操作。如果下次你要用到一個存儲字符串類型的鏈表,就只好把上面的過程重新來過。也許你覺得這個在原有代碼基礎(chǔ)上做略微修改的過程并不復(fù)雜,可是也許我們會不斷的增加對于鏈表這個數(shù)據(jù)結(jié)構(gòu)的操作,而需要用鏈表來存儲的數(shù)據(jù)類型也越來越多,這些都意味著海量的代碼和繁瑣的后期維護(hù)工作。當(dāng)你有了上百個存儲不同數(shù)據(jù)類型的鏈表結(jié)構(gòu),每當(dāng)你要增加一個操作,或者修改某個操作的傳入?yún)?shù),工作量會變大到像一場災(zāi)難。

但是我們可以改造上述代碼,讓它能夠處理你所想要讓它處理的任何數(shù)據(jù)類型:實(shí)行,字符型,乃至任何你自己定義的 structure 類型。

Void*:萬能的指針“掛鉤”

幾乎所有講授 C 語言課程的老師都會告訴你:“指針是整個 C 語言的精髓所在。”而你也一直敬畏著指針,又愛又恨地使用著它。許多教材都告訴你,int * 叫做指向整型的指針,而 char * 是指向字符型的指針,等等不一而足。然而這里有一個另類的指針家族成員—— void *。不要按照通常的命名方式叫它做指向 void 類型的指針,它的正式的名字叫做:可以指向任意類型的指針。你一定注意到了“任意類型”這四個字,沒錯,實(shí)現(xiàn)多態(tài),我們靠的就是它。

下面來改造我們的鏈表代碼,在 linkList.h 里,如下:

typedef struct Node* linkList;

struct Node                    // 鏈表節(jié)點(diǎn)

{

void *data;                // 存儲的數(shù)據(jù)指針

linkList next;              // 指向下一個鏈表節(jié)點(diǎn)

};

linkList initialLinklist();               // 初始化鏈表

link newLinkList?。╲oid *data);          // 建立新節(jié)點(diǎn)

void insertFirst(linkList h, void *data);     // 在已有鏈表的表頭進(jìn)行插入節(jié)點(diǎn)操作

void linkListOutput(linkList h);             // 輸出鏈表中數(shù)據(jù)到控制臺

我們來看看現(xiàn)在這個鏈表和剛才那個只能存儲整型數(shù)據(jù)的鏈表的區(qū)別。

當(dāng)你把 Node 結(jié)構(gòu)體里面的成員定義為一個整型數(shù)據(jù),就好像把這個鏈表節(jié)點(diǎn)打造成了一個大小形狀固定的盒子,你定義一個鏈表節(jié)點(diǎn),程序進(jìn)行編譯的時候編譯器就為你打造一個這樣的盒子:裝一個 int 類型的數(shù)據(jù),然后裝一個 linkList 類型的指針。如果你想強(qiáng)行在這個盒子里裝別的東西,編譯器會告訴你,對不起,盒子的大小形狀并不合適。所以你必須為了裝各種各樣類型的數(shù)據(jù)打造出不同的生產(chǎn)盒子的流水線,想要裝哪種類型數(shù)據(jù)的盒子,就開啟對應(yīng)的流水線來生產(chǎn)。

但是當(dāng)你把結(jié)構(gòu)體成員定義為 void *,一切都變得不同了。這時的鏈表節(jié)點(diǎn)不再像個大小形狀固定的盒子,而更像一個掛鉤,它可以掛上一個任意類型的數(shù)據(jù)。不管你需要存儲什么類型的數(shù)據(jù),你只要傳遞一個指針,把它存儲到 Node 節(jié)點(diǎn)中去,就相當(dāng)于把這個數(shù)據(jù)“掛”了上去,無論何時你都可以根據(jù)指針找到它。這時的鏈表仿佛變成了一排粘貼在墻上的衣帽鉤,你可以掛一排大衣,可以掛一排帽子,可以掛一排圍巾,甚至,你可以并排掛一件大衣一頂帽子一條圍巾在墻上。void * 初露猙獰,多態(tài)離 C 語言并不遙遠(yuǎn)。

 

 

實(shí)現(xiàn):你的多態(tài)你做主

當(dāng)你真正開始著手做這個工作的時候,你會發(fā)現(xiàn)把數(shù)據(jù)放入鏈表中的操作和普通的存放 int 類型的鏈表的實(shí)現(xiàn)并沒有什么大的區(qū)別,很方便。但是當(dāng)你要把已經(jīng)存進(jìn)去的數(shù)據(jù)讀取出來的時候,就有一點(diǎn)麻煩了。對于 void * 類型的指針,編譯器只知道它里面存儲了一個地址,但是關(guān)于這個地址里的數(shù)據(jù)類型,編譯器是沒有任何概念的。畢竟我們不能指望編譯器什么都知道,什么都能替你做好,所以存進(jìn)去的數(shù)據(jù)的類型,作為程序員的我們必須清楚的知道,并且在取出這個數(shù)據(jù)的時候,用這一類型的指針來對 void * 做強(qiáng)制類型轉(zhuǎn)換。

為了方便的做到這一點(diǎn),我采取的方法是在 Node 結(jié)構(gòu)體中增加一個標(biāo)識數(shù)據(jù)類型的域,并用一個枚舉類型來存放這些數(shù)據(jù)類型。這時的 linkList.h 如下所示:

#ifndef LINKLIST_H

#define LINKLIST_H

typedef struct Node* linkList;

enum dataType

{

INT,

DOUBLE,

CHAR,

STRING

};

struct Node                        // 鏈表節(jié)點(diǎn)

{

void *data;                    // 存儲的數(shù)據(jù)指針

int dataType;                   // 存儲數(shù)據(jù)類型

linkList next;                  // 指向下一個鏈表節(jié)點(diǎn)

};

linkList initialLinklist();                // 初始化鏈表

linkList newLinkList?。╲oid *data, int dataType);     // 建立新節(jié)點(diǎn)

void insertFirst(linkList h, void *data, int dataType);  // 在已有鏈表的表頭進(jìn)行插入節(jié)點(diǎn)操作

void linkListOutput(linkList h);             // 輸出鏈表中數(shù)據(jù)到控制臺

#endif

初始化鏈表,代碼如下:

linkList initialLinklist()

{

linkList link =?。╨inkList*)malloc(sizeof(*link));

link-》data = NULL;

link-》dataType = -1;

link-》next = NULL;

return link;

}

 

 

建立新節(jié)點(diǎn),代碼如下:

linkList newLinkList?。╲oid *data, int dataType)

{

linkList link =?。╨inkList*)malloc(sizeof(*link));

link-》data = data;

link-》dataType = dataType;

link-》next = NULL;

return link;

}

在已有鏈表的表頭進(jìn)行插入節(jié)點(diǎn)操作,代碼如下:

void insertFirst(linkList h, void *data, int dataType)

{

linkList l = newLinkList(data, dataType);

l-》next = h-》next;

h-》next = l;

}

輸出鏈表中數(shù)據(jù)到控制臺,代碼如下:

void linkListOutput(linkList h)

{

linkList p = h;

p = p-》next;

while(p??!= NULL)

{

switch(p-》dataType)

{

case 0:

{

p

溫馨提示:為不影響您的學(xué)業(yè),來校區(qū)前請先電話咨詢,方便我校安排相關(guān)的專業(yè)老師為您解答
姓名不能為空
手機(jī)號格式錯誤