0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

不完全類型和抽象數(shù)據(jù)類型的定義

AGk5_ZLG_zhiyua ? 來源:未知 ? 作者:佚名 ? 2017-09-14 14:44 ? 次閱讀

立功教授數(shù)年之心血之作《程序設(shè)計(jì)與數(shù)據(jù)結(jié)構(gòu)》以及《面向AMetal框架與接口編程(上)》,書本內(nèi)容公開后,在電子行業(yè)掀起一片學(xué)習(xí)熱潮。經(jīng)周立功教授授權(quán),本公眾號(hào)特對(duì)《程序設(shè)計(jì)與數(shù)據(jù)結(jié)構(gòu)》一書內(nèi)容進(jìn)行連載,愿共勉之。

第二章為程序設(shè)計(jì)技術(shù),本文為2.4.1 不完全類型和2.4.2 抽象數(shù)據(jù)類型。

>>> 2.4.1 不完全類型

不完全類型是指“函數(shù)之外、類型的大小不能被確定的類型”,結(jié)構(gòu)體標(biāo)記的聲明就是一個(gè)不完全類型的典型示例。比如:

此時(shí),struct _TypeA和struct _TypeB是互相引用的,雖然無論先聲明哪一邊都很麻煩,但可以先通過聲明結(jié)構(gòu)體標(biāo)記回避以上問題。比如:

當(dāng)使用typedef聲明結(jié)構(gòu)體類型時(shí),比如:

由于TypeB類型的標(biāo)記被聲明時(shí),還不知道它的內(nèi)容,因此無法確定它的大小,這樣的類型就被稱為不完全類型。因?yàn)椴荒艽_定大小,所以不能將不完全類型變成數(shù)組,也不能將其作為結(jié)構(gòu)體的成員,或聲明為變量。但如果聲明為指針,則可以使用不完全類型。在后續(xù)定義struct _TypeB的內(nèi)容時(shí),TypeB就不是不完全類型了。

通常在“.h”頭文件中聲明不包含任何實(shí)現(xiàn)細(xì)節(jié)的結(jié)構(gòu)體,然后在“.c”實(shí)現(xiàn)文件中定義與數(shù)據(jù)結(jié)構(gòu)的特定實(shí)現(xiàn)配合使用的函數(shù)。數(shù)據(jù)結(jié)構(gòu)的用戶可以看到聲明和函數(shù)原型,但實(shí)現(xiàn)會(huì)隱藏在“.c”文件中。只有使用數(shù)據(jù)結(jié)構(gòu)所需要的信息會(huì)對(duì)用戶可見,如果太多的內(nèi)部信息可見,用戶可能會(huì)使用這些信息從而產(chǎn)生依賴。一旦內(nèi)部結(jié)構(gòu)發(fā)生變化,則用戶代碼可能就會(huì)失效。不完全類型是因?yàn)榫幾g器看不見“.c”文件中的實(shí)際定義,它只能看到_demoB結(jié)構(gòu)體的類型定義,而看不見結(jié)構(gòu)體的實(shí)現(xiàn)細(xì)節(jié)。

下面將以數(shù)組為例介紹不完全類型的使用,盡管可以使用數(shù)組保存元素,由于數(shù)組的大小是固定的,因此數(shù)組并不會(huì)存儲(chǔ)它的大小,而且也不會(huì)檢查下標(biāo)是否越界。通常用一個(gè)指向數(shù)組的指針pBuffer和記錄數(shù)組元素個(gè)數(shù)的值count代替數(shù)組。其實(shí)現(xiàn)如下(IA_array.c):

為了防止用戶直接訪問結(jié)構(gòu)體的成員,通常將結(jié)構(gòu)體移到實(shí)現(xiàn)代碼中(IA_array.c)隱藏起來,然后使用不完全的類型在接口中(IA_array.h)聲明一個(gè)IntArray處理相應(yīng)的數(shù)據(jù)。雖然不完全類型描述了對(duì)象,但缺少對(duì)象大小所需的信息。比如:

其告訴編譯器_IntArray是一個(gè)結(jié)構(gòu)體標(biāo)記,卻沒有描述結(jié)構(gòu)體的成員,因此編譯器沒有足夠的信息確定結(jié)構(gòu)體的大小,其意圖是不完全類型將會(huì)在程序的其它地方將信息補(bǔ)充完整。不完全類型的使用是受限的,因?yàn)榫幾g器不知道它的大小,所以不能在接口中用它聲明變量:

但可以在接口中(IA_array.h)定義一個(gè)指針類型引用不完全類型:

在這里,僅僅聲明了它的存在,而沒有做別的任何事情。對(duì)于用戶來說,看到的只是IA_array.h,而對(duì)_IntArray的構(gòu)造或?qū)崿F(xiàn)卻一無所知。

當(dāng)將IntArray定義為一個(gè)指向struct _IntArray結(jié)構(gòu)體類型時(shí),即可聲明IntArray*類型的變量,將其作為函數(shù)參數(shù)進(jìn)行傳遞。即:

盡管此時(shí)還沒有定義IntArray,但指針的大小始終相同,且不依賴于它指向的對(duì)象。即便在不知道結(jié)構(gòu)體本身細(xì)節(jié)的前提下,編譯器同樣允許處理指向結(jié)構(gòu)體的指針,這就解釋了為什么C允許這種行為。雖然這個(gè)結(jié)構(gòu)體是一個(gè)不完全類型,但在實(shí)現(xiàn)代碼中信息變得完整,因此該結(jié)構(gòu)體的成員依賴于實(shí)現(xiàn)方法。

雖然數(shù)組和指針有區(qū)別,但C語言不會(huì)區(qū)分它們,C對(duì)數(shù)組提供的支持只是為了便于內(nèi)存管理和指針運(yùn)算,最好的證明莫過于括號(hào)運(yùn)算符居然有交換性。當(dāng)在結(jié)構(gòu)體內(nèi)創(chuàng)建一個(gè)數(shù)組時(shí),為了避免直接對(duì)數(shù)據(jù)進(jìn)行訪問,將通過接口函數(shù)和對(duì)象交互,詳見程序清單 2.30。

程序清單 2.30 訪問數(shù)組元素和大小接口(IntArray.h)

為了說明這些函數(shù)構(gòu)成了IntArray對(duì)象的接口,并防止函數(shù)名和其它對(duì)象的接口沖突,于是在每個(gè)函數(shù)名前使用了前綴IA_,且每個(gè)函數(shù)都有一個(gè)IntArray*型的對(duì)象作為參數(shù),這個(gè)參數(shù)就是函數(shù)將要操作的對(duì)象。

IA_init()初始化使數(shù)組處于空元素的狀態(tài),IA_cleanup()釋放在數(shù)組生存期中分配給用戶的內(nèi)存。剩余的函數(shù)是控制對(duì)數(shù)組中數(shù)據(jù)的訪問,IA_setSize()設(shè)置了數(shù)組中的元素個(gè)數(shù),且為這些元素分配存儲(chǔ)空間,IA_getSize()返回當(dāng)前數(shù)組中的元素個(gè)數(shù)。IA_setElem()和IA_getElem()用于訪問單個(gè)數(shù)據(jù)元素,其具體實(shí)現(xiàn)詳見程序清單 2.31。

程序清單 2.31 訪問數(shù)組元素和大小接口的實(shí)現(xiàn)(IntArray.c)

其中,IA_setSize()用于改變數(shù)組大小,首先釋放原有的元素,然后保存新的元素,且為新元素分配存儲(chǔ)空間。當(dāng)然,也可以優(yōu)化進(jìn)一步代碼,比如,只有數(shù)組大小增加時(shí)才重新分配空間。IA_getSize()訪問給定下標(biāo)所指的元素,讓IntArray檢查下標(biāo)是否在界內(nèi)。

由此可見,IntArray的實(shí)現(xiàn)是由兩部分組成的,即保存對(duì)象信息的數(shù)據(jù)和構(gòu)成對(duì)象接口的函數(shù),其使用范例程序詳見程序清單 2.32。

程序清單 2.32 使用IntArray.h范例程序

>>> 2.4.2 抽象數(shù)據(jù)類型

1. 棧的實(shí)現(xiàn)

假設(shè)需要一個(gè)字符棧,且棧的大小是固定的,即可使用數(shù)組保存棧中的元素,然后指定一個(gè)計(jì)數(shù)器表明棧中元素的數(shù)量。其數(shù)據(jù)結(jié)構(gòu)定義如下:

由于調(diào)用者并不能直接訪問底層,因此在向棧中壓入元素之前,必須先創(chuàng)建一個(gè)棧。其函數(shù)原型為:

由于剛開始時(shí)棧為空,暫時(shí)還沒有元素存儲(chǔ)到數(shù)組elements[0]中,因此只要將數(shù)組的下標(biāo)置為0,即可創(chuàng)建一個(gè)空棧。即:

其調(diào)用形式如下:

當(dāng)向棧中壓入一個(gè)新的元素時(shí),將元素存儲(chǔ)在數(shù)組接下來的空間中,并計(jì)數(shù)遞增。其函數(shù)原型為:

也就是說,當(dāng)top的值加1時(shí),則將新的元素值value入棧。即:

當(dāng)彈出元素時(shí),計(jì)數(shù)遞減并返回棧頂元素。其函數(shù)原型如下:

也就是說,當(dāng)top的值減1時(shí),則刪除棧頂結(jié)點(diǎn),返回該結(jié)點(diǎn)的值。比如:

除了這些基本的操作之外,經(jīng)常還需要知道棧所包含的元素?cái)?shù)量,以及棧是空還是滿,這些函數(shù)的原型為:

顯然,只要返回棧頂值就知道棧中存儲(chǔ)了多少個(gè)元素,而當(dāng)stack->top為0時(shí),說明棧為空;當(dāng)stack->top大于等于MAXSIZE時(shí),說明棧已滿。

實(shí)際上,當(dāng)定義了一個(gè)結(jié)構(gòu)體指針變量stack后,(stack->top)就成為了一個(gè)變量,即可通過stack->top與stack->elements[stack->top++]分別實(shí)現(xiàn)對(duì)stack的各成員的訪問。顯然程序暴露了“數(shù)組和下標(biāo)”這一內(nèi)部結(jié)構(gòu),且無法阻止用戶使用stack指針變量直接訪問結(jié)構(gòu)體的成員。比如:

由于對(duì)直接訪問top和elements,因此用戶有可能破壞棧中的數(shù)據(jù)。如果其內(nèi)部實(shí)現(xiàn)發(fā)生變化,也必須對(duì)程序進(jìn)行相應(yīng)的修改。如果程序規(guī)模很大,則修改的工作量也很大,因此很多時(shí)候明明知道通過重構(gòu)能夠改善程序,也會(huì)因工作量太大而不愿意改變具體的實(shí)現(xiàn)。

由此可見,上述棧的實(shí)現(xiàn)方法不僅暴露了棧的數(shù)據(jù)結(jié)構(gòu),而且僅有1個(gè)棧。如果需要多個(gè)棧時(shí),怎么辦?一種方法是編寫多個(gè)名字不同功能相同的函數(shù),這樣就會(huì)出現(xiàn)多段處理完全相同的代碼。為了解決這個(gè)問題,抽象的方法是將棧中的數(shù)據(jù)結(jié)構(gòu)隱藏到實(shí)現(xiàn)代碼中。

2. 建立抽象

雖然標(biāo)準(zhǔn)C提供了類似int、char、float、double這樣不可分割的原子數(shù)據(jù)類型,但如果需要表示任意大的整數(shù),顯然原子數(shù)據(jù)類型無能為力。此時(shí),創(chuàng)建一種新的整數(shù)類型勢(shì)在必行,而這種新的數(shù)據(jù)類型便是一種抽象數(shù)據(jù)類型ADT(Abstract Data Type)。

設(shè)計(jì)一個(gè)基于Stack的抽象數(shù)據(jù)類型,我們應(yīng)該從哪里開始呢?一個(gè)不錯(cuò)的方法是用一句話來描述。這種描述應(yīng)該盡可能地抽象,盡量不要涉及數(shù)據(jù)的內(nèi)部結(jié)構(gòu),要簡單到誰都能夠理解它,因此可以描述“棧(Stack)是一個(gè)可以在同一個(gè)位置上插入(push)和刪除(pop)數(shù)據(jù)(value)的存儲(chǔ)器,該位置是存儲(chǔ)器的末端,即棧頂(top)”。該定義既未說明棧中存儲(chǔ)什么數(shù)據(jù),也未指定是用數(shù)組、結(jié)構(gòu)體還是其它數(shù)據(jù)形式存儲(chǔ)數(shù)據(jù),而且也沒有規(guī)定用什么方式實(shí)現(xiàn)操作,這些細(xì)節(jié)都留給實(shí)現(xiàn)去完成。

關(guān)于棧的詳細(xì)描述如下:

  • 類型名:Stack

  • 類型屬性:可以存儲(chǔ)有序的數(shù)據(jù)(value)

  • 類型操作:創(chuàng)建棧(newStack)和銷毀棧(freeStack),從棧頂添加數(shù)據(jù)(push)和從棧頂刪除數(shù)據(jù)(pop),確定棧是否為空(stackIsEmpty),確定棧是否已滿(stackIsFull),返回棧中元素的個(gè)數(shù)(getStackDepth),讀取棧中任何位置的元素(getStackElement)。

也就是說,在向棧中添加元素之前,必須先創(chuàng)建一個(gè)棧。當(dāng)不再使用內(nèi)存時(shí),必須銷毀棧。對(duì)棧的基本操作有push(進(jìn)棧)和pop(出棧),前者相當(dāng)于插入,后者相當(dāng)于刪除最后插入的元素。對(duì)空棧進(jìn)行的pop,認(rèn)為是棧ADT的錯(cuò)誤。另一方面,當(dāng)運(yùn)行push時(shí)空間用盡是一個(gè)實(shí)現(xiàn)錯(cuò)誤,但不是ADT錯(cuò)誤。

3. 建立接口

(1)隔離變化

為了防止用戶直接訪問top和elements而破壞棧中的數(shù)據(jù),根據(jù)以往的經(jīng)驗(yàn),可以使用使用依賴倒置原則。將保存在結(jié)構(gòu)體中棧的實(shí)現(xiàn)所需要的數(shù)據(jù)結(jié)構(gòu)隱藏在“.c”文件中,將處理數(shù)據(jù)的接口包含在“.h”文件中,用戶將無法看到棧的數(shù)據(jù)結(jié)構(gòu)在底層是如何實(shí)現(xiàn)的。

雖然可以將一個(gè)數(shù)組看作是具有固定小的,但是內(nèi)置數(shù)組并不會(huì)存儲(chǔ)它的大小,而且也不會(huì)檢查下標(biāo)是否越界。通常將一個(gè)指向數(shù)組的指針data和記錄數(shù)組元素個(gè)數(shù)的值numData存儲(chǔ)棧的最大容量,以及記錄棧頂元素的位置top進(jìn)行打包,將棧的數(shù)據(jù)結(jié)構(gòu)隱藏在“.c”文件中。即:

對(duì)于用戶來說,現(xiàn)在只能通過“.h”文件中的接口操作棧。盡管此時(shí)還沒有定義stackCDT,由于指針的大小始終相同,且不依賴于它指向的對(duì)象。即便在不知道結(jié)構(gòu)體本身細(xì)節(jié)的前提下,編譯器同樣允許處理指向結(jié)構(gòu)體的指針,因此可以定義一個(gè)指針類型引用不完全類型,將stackADT定義為一個(gè)指向stackCDT *結(jié)構(gòu)體類型。比如:

雖然這個(gè)結(jié)構(gòu)是一個(gè)不完全類型,但在實(shí)現(xiàn)棧的文件中信息變得完整,因此該結(jié)構(gòu)的成員依賴于棧的實(shí)現(xiàn)方法。stackADT結(jié)構(gòu)體類型的變量定義如下:

由于一個(gè)stack1指向一個(gè)存儲(chǔ)單元,即一個(gè)存儲(chǔ)單元代表一個(gè)棧,因此你想要多少個(gè)棧就有多少個(gè)棧。比如:

顯而易見,stackADT是代表stack1、stack2、stack3等所有具體棧的總稱的抽象數(shù)據(jù)類型,stack1、stack2和stack3分別指向不同的棧。因此只要將stack1、stack2和stack3作為實(shí)參傳遞給相應(yīng)的函數(shù),即可訪問與之相應(yīng)的棧。而抽象的方法是在棧的實(shí)現(xiàn)代碼和使用棧的代碼之間添加一個(gè)函數(shù)層。比如:

通常將稱為函數(shù)上下文的stackADT類型的stack作為函數(shù)的第一個(gè)參數(shù),這個(gè)參數(shù)就是函數(shù)將要操作的對(duì)象。它代表指向當(dāng)前對(duì)象(棧)的指針,用于請(qǐng)求對(duì)象對(duì)自身執(zhí)行某些操作。而結(jié)構(gòu)體的成員變量就是通過stack指針找到自己所屬的對(duì)象的,其引用方式如下:

由此可見,用戶僅通過接口函數(shù)與棧交互,而不是直接訪問它的數(shù)據(jù)。

(2)操作方法

  • 創(chuàng)建棧

由于用戶完全不知道底層是如何表示的,因此必須提供一個(gè)用于創(chuàng)建一個(gè)新stackADT的函數(shù),且將它返回給用戶。用于創(chuàng)建一個(gè)新的抽象類型的值的函數(shù)名稱以new開始,以強(qiáng)調(diào)動(dòng)態(tài)分配。其函數(shù)原型如下:

前置條件:stackADT被定義為一個(gè)指向結(jié)構(gòu)體的指針,該結(jié)構(gòu)體包含top和numData。一旦知道最大容量,則該棧即可被動(dòng)態(tài)確定。創(chuàng)建一個(gè)具有給定最大值MAXSIZE的棧,其分別是為stackCDT結(jié)構(gòu)體分配空間和長度為MAXSIZE的數(shù)組分配空間。同時(shí)將top初始化為0,并將numData置為最大值MAXSIZE。

后置條件:返回棧。

其調(diào)用形式如下:

  • 銷毀棧

當(dāng)接口定義了一個(gè)分配新的抽象類型的值的函數(shù)時(shí),通常還要為接口提供一個(gè)用于釋放用戶不再使用的棧的動(dòng)態(tài)內(nèi)存的函數(shù)。其函數(shù)原型如下:

前置條件:stack指向之前創(chuàng)建的棧;

后置條件:釋放動(dòng)態(tài)分配的所有內(nèi)存,即先釋放棧的數(shù)組,然后釋放棧的結(jié)構(gòu)。

其調(diào)用形式如下:

  • 從棧頂添加據(jù)(進(jìn)棧)

當(dāng)用戶向棧頂添加一個(gè)數(shù)據(jù)時(shí),就是將該值存儲(chǔ)在內(nèi)部的數(shù)據(jù)結(jié)構(gòu)中。即通過在容器的頂端插入元素實(shí)現(xiàn)push,其函數(shù)原型如下:

前置條件:stack指向之前創(chuàng)建的棧,value是待壓入棧頂?shù)臄?shù)據(jù);

后置條件:如果棧不滿,將value放在棧頂,該函數(shù)返回true,否則棧不變,該函數(shù)返回false。

其調(diào)用形式如下:

  • 從棧頂刪除數(shù)據(jù)(出棧)

當(dāng)用戶彈出棧元素時(shí),就是將存儲(chǔ)的值返回給用戶。即通過刪除容器頂端的元素實(shí)現(xiàn)pop,其函數(shù)原型如下:

前置條件:stack指向之前創(chuàng)建的棧,pValue為指向存儲(chǔ)返回值變量的指針;

后置條件:如果棧不空,將棧頂?shù)闹悼截惖?pValue,刪除棧頂?shù)闹?,該函?shù)返回true,如果刪除前棧為空,棧不變,該函數(shù)返回false。

其調(diào)用形式如下:

  • 判斷棧是否為空

判斷棧是否為空的函數(shù)原型如下:

前置條件:stack指向之前創(chuàng)建的棧;

后置條件:如果棧為空則返回true,否則返回false。

其調(diào)用形式如下:

  • 判斷棧是否已滿

判斷棧是否已滿的函數(shù)原型如下:

前置條件:stack指向之前創(chuàng)建的棧;

后置條件:如果棧已滿則返回true,否則返回false。

其調(diào)用形式如下:

  • 確定棧中元素的個(gè)數(shù)

確定棧中元素的個(gè)數(shù)的函數(shù)原型如下:

前置條件:stack指向之前創(chuàng)建的棧;

后置條件:返回棧中元素的個(gè)數(shù)。

其調(diào)用形式如下:

  • 讀取棧中任何位置的元素

讀取棧棧任意位置元素的函數(shù)原型如下:

前置條件:stack指向之前創(chuàng)建的棧,index為索引值,表示返回棧中某個(gè)位置的元素, pValue為指向存儲(chǔ)返回值變量的指針;

后置條件:如果index大于top,該函數(shù)返回false,反之將index位置的值拷貝到*pValue,該函數(shù)返回true。

其調(diào)用形式如下:

由于數(shù)組的下標(biāo)是從0開始的,當(dāng)index為0時(shí),則getStackElemnt(stack, 0, &temp)返回棧頂?shù)脑?,getStackElemnt(stack, 1, &temp)返回接下來的那個(gè)元素,依此類推。

封裝時(shí),頭文件中只放最小的接口函數(shù)聲明,且內(nèi)部函數(shù)都要加上static關(guān)鍵字。抽象棧的接口詳見程序清單 2.33,接口揭示了棧的數(shù)據(jù)類型和用戶在操作棧時(shí)需要的各種功能,這些功能實(shí)現(xiàn)了抽象棧類型的基本操作。

程序清單 2.33 抽象棧接口(stack.h)

這些函數(shù)共同創(chuàng)建了接口,每個(gè)函數(shù)都以stackADT作為它的第一個(gè)參數(shù)。當(dāng)聲明了函數(shù)接口后,即可實(shí)現(xiàn)相應(yīng)的接口。

4. 實(shí)現(xiàn)接口

由于數(shù)組的長度在編譯時(shí)就已經(jīng)確定了,無法在運(yùn)行時(shí)動(dòng)態(tài)地調(diào)整。但有些應(yīng)用在編譯時(shí)并不知道應(yīng)該分配多大的內(nèi)存空間才能滿足要求,因此可以根據(jù)需要使用動(dòng)態(tài)內(nèi)存“在運(yùn)行時(shí)”為它分配內(nèi)存空間。和任何接口一樣,實(shí)現(xiàn)Stack.h接口需要編寫一個(gè)模塊Stack.c,它提供了抽象類型的輸出函數(shù)和表示細(xì)節(jié)的代碼,詳見程序清單 2.34。

程序清單 2.34 抽象棧的實(shí)現(xiàn)(Stack.c)

表面上看起來getStackDepth()函數(shù)只有一行代碼,也許有人會(huì)說,為何不直接使用“stack->top;”代替該函數(shù)呢?如果用戶在程序中使用top,那么程序?qū)⒁蕾囉趕tackADT表示的具體結(jié)構(gòu),而使用該函數(shù)的好處是為用戶和實(shí)現(xiàn)之間提供了隔離層。由于維護(hù)代碼是軟件工程生命周期中的一個(gè)重要步驟,因此要盡量做好隨時(shí)修改的準(zhǔn)備。

當(dāng)然,上述程序還是不能創(chuàng)建兩種數(shù)據(jù)類型不同的棧,最常見的方法是使用void *作為數(shù)據(jù)類型,這樣就可以壓入和彈出任意類型的指針了。這里不再詳細(xì)描述,將留給讀者自己實(shí)現(xiàn)。但使用void *作為數(shù)據(jù)類型的最大缺點(diǎn)是不能進(jìn)行錯(cuò)誤檢測(cè),存放void *數(shù)據(jù)的棧允許各種類型的指針共存,因此無法檢測(cè)由壓入錯(cuò)誤的指針類型而導(dǎo)致的錯(cuò)誤。

5. 使用接口

實(shí)際上使用棧的人并不關(guān)心棧是如何實(shí)現(xiàn)的,即使要改變棧的內(nèi)部實(shí)現(xiàn)方式,也不用對(duì)使用棧的程序做任何修改。將整數(shù)推入棧,然后再打印輸出的范例程序詳見程序清單 2.35。

程序清單 2.35 使用棧接口的范例程序

綜上所述,Stack棧的接口分為兩部分,其一是描述如何表示數(shù)據(jù),其二是描述實(shí)現(xiàn)ADT操作的函數(shù),因此必須先提供存儲(chǔ)數(shù)據(jù)的方法。設(shè)計(jì)一個(gè)結(jié)構(gòu)體,在“.h”接口中定義棧的抽象數(shù)據(jù)類型stackADT,在“.c”實(shí)現(xiàn)中定義棧的具體類型stackCDT。其次必須提供管理該數(shù)據(jù)的函數(shù)(方法),通過函數(shù)原型隱藏它們的底層實(shí)現(xiàn)。只要保留它們的接口不變,對(duì)于任何抽象都可以改變它的實(shí)現(xiàn)。實(shí)際上,當(dāng)引入一個(gè)抽象數(shù)據(jù)類型stackADT時(shí),就是在使用依賴倒置原則,將保存在結(jié)構(gòu)體中棧的實(shí)現(xiàn)所需要的數(shù)據(jù)和處理數(shù)據(jù)的接口徹底分離,因?yàn)閟tackADT沒有暴露它的細(xì)節(jié),用戶依賴于satcADT抽象,而不是細(xì)節(jié)。

顯然,抽象數(shù)據(jù)類型可利用已經(jīng)存在的原子數(shù)據(jù)類型構(gòu)造新的結(jié)構(gòu),用已經(jīng)實(shí)現(xiàn)的操作組合新的操作。對(duì)于ADT,用戶程序除了通過接口中提到的那些操作之外,并不訪問任何數(shù)據(jù)值。數(shù)據(jù)的表示和實(shí)現(xiàn)操作的函數(shù)都在接口的實(shí)現(xiàn)里面,與用戶完全分離。抽象的接口隱臧不相關(guān)的細(xì)節(jié),用戶不能通過接口看到方法的實(shí)現(xiàn),將注意力集中在本質(zhì)特征上,將程序員從關(guān)心程序如何實(shí)現(xiàn)的細(xì)節(jié)上得到解放。對(duì)于任何抽象來說,只要保持接口不變,我們可以根據(jù)需要改變其實(shí)現(xiàn)方式。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴

原文標(biāo)題:周立功:不完全類型和抽象數(shù)據(jù)類型的使用

文章出處:【微信號(hào):ZLG_zhiyuan,微信公眾號(hào):ZLG致遠(yuǎn)電子】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 2人收藏

    評(píng)論

    相關(guān)推薦

    GaussDB 數(shù)據(jù)類型介紹

    進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換,以滿足不同的需求。本文將以示例的形式羅列并介紹一些常見的數(shù)據(jù)類型轉(zhuǎn)換方法等。? 數(shù)據(jù)類型概念及特點(diǎn) 數(shù)據(jù)類型是一組值的集合以及定義
    的頭像 發(fā)表于 06-05 16:40 ?1853次閱讀
    GaussDB <b class='flag-5'>數(shù)據(jù)類型</b>介紹

    LabVIEW自定義數(shù)據(jù)類型

    一直只知道自定義控件,不知道自定義數(shù)據(jù)類型,直到有一天看到別人的后面板某控件左上角有個(gè)黑色小三角形,像這樣,才知道有自定義數(shù)據(jù)類型,類似于C
    發(fā)表于 03-24 17:24

    51單片機(jī)中的數(shù)據(jù)類型解析

    數(shù)據(jù)類型數(shù)據(jù)結(jié)構(gòu)中的定義是一個(gè)值的集合以及定義在這個(gè)值集上的一組操作。數(shù)據(jù)類型包括原始類型、多
    發(fā)表于 11-16 08:45 ?2.5w次閱讀
    51單片機(jī)中的<b class='flag-5'>數(shù)據(jù)類型</b>解析

    vhdl數(shù)據(jù)類型

    VHDL中的標(biāo)識(shí)符可以是常數(shù)、變量、信號(hào)、端口、子程序或參數(shù)的名字。VHDL中的數(shù)據(jù)類型可以分成四大類: 標(biāo)量型(SCALAR TYPE):屬單元素的最基本的數(shù)據(jù)類型,通常用于描述一個(gè)單值數(shù)據(jù)對(duì)象
    發(fā)表于 03-30 15:59 ?11次下載

    5個(gè)簡單步驟在C中創(chuàng)建抽象數(shù)據(jù)類型

    對(duì)于許多軟件開發(fā)人員來說,面向?qū)ο缶幊淌且粋€(gè)很好的工具。遺憾的是,使用過程C編程語言的嵌入式軟件工程師在許多現(xiàn)代編程語言功能上都失敗了。抽象數(shù)據(jù)類型(通常簡稱為ADT)是數(shù)據(jù)類型,其實(shí)現(xiàn)細(xì)節(jié)隱藏在數(shù)據(jù)結(jié)構(gòu)的用戶視圖中,但ADT可
    的頭像 發(fā)表于 08-07 14:40 ?3141次閱讀

    C語言的數(shù)據(jù)儲(chǔ)存與數(shù)據(jù)類型類型轉(zhuǎn)換的詳細(xì)資料說明

    程序說到底就是對(duì)數(shù)據(jù)的處理,所以首先要弄清楚需要處理哪些數(shù)據(jù),計(jì)算機(jī)如何存儲(chǔ)這些數(shù)據(jù)。C語言根據(jù)需要,抽象出了一些基本數(shù)據(jù)類型和衍生
    的頭像 發(fā)表于 02-24 15:39 ?4117次閱讀
    C語言的<b class='flag-5'>數(shù)據(jù)</b>儲(chǔ)存與<b class='flag-5'>數(shù)據(jù)類型</b>及<b class='flag-5'>類型</b>轉(zhuǎn)換的詳細(xì)資料說明

    重視變量的數(shù)據(jù)類型

    不管在什么語言中,定義一個(gè)變量時(shí)必然要在內(nèi)存中開辟一個(gè)相應(yīng)大小的空間來存儲(chǔ)該變量。不同的數(shù)據(jù)類型在內(nèi)存所占的空間大小不同,其所能表示的數(shù)據(jù)范圍也不相同。在單片機(jī)C語言中,常用的基本數(shù)據(jù)類型
    發(fā)表于 01-13 15:05 ?1次下載
    重視變量的<b class='flag-5'>數(shù)據(jù)類型</b>

    結(jié)構(gòu)數(shù)據(jù)類型(Struct)及應(yīng)用案例

    Struct數(shù)據(jù)類型使用非常靈活,隨時(shí)可以使用,但是相對(duì)于PLC數(shù)據(jù)類型 (UDT) 有以下缺點(diǎn),所以建議需要使用Struct類型時(shí),可以使用PLC數(shù)據(jù)類型(UDT)代替。
    的頭像 發(fā)表于 07-27 16:10 ?2040次閱讀

    用戶定義數(shù)據(jù)類型的結(jié)構(gòu)

    用戶定義數(shù)據(jù)類型(UDTs)是你自己創(chuàng)建的特殊數(shù)據(jù)結(jié)構(gòu)。因用戶數(shù)據(jù)類型指派了名字,他們可以用很多次。一旦他們被定義,就可在CPU程序的任意點(diǎn)
    的頭像 發(fā)表于 08-19 10:06 ?1651次閱讀

    什么是數(shù)據(jù)類型轉(zhuǎn)換

    常用的3種數(shù)據(jù)類型:1、Python數(shù)據(jù)類型第一種:字符串(str)。 2、Python數(shù)據(jù)類型第二種:整數(shù)(int)。 3、Python數(shù)據(jù)類型第三種:浮點(diǎn)數(shù)(float)。
    的頭像 發(fā)表于 02-23 15:21 ?1873次閱讀

    定義數(shù)據(jù)類型

    在運(yùn)算之前我們必須首先定義數(shù)據(jù)類型,定義出腳本支持的數(shù)據(jù)類型,這是運(yùn)算的基礎(chǔ)。 這一小節(jié)我們將定義
    的頭像 發(fā)表于 03-03 10:10 ?1116次閱讀

    博途PLC1200/1500PLC用戶自定義數(shù)據(jù)類型(UDT)

    用戶自定義數(shù)據(jù)類型可以包含基本數(shù)據(jù)類型(例如,INT bool string),以及 數(shù)組 ,結(jié)構(gòu)體,以及PLC的專有數(shù)據(jù)類型等,而且用戶自定義
    發(fā)表于 04-20 09:46 ?5次下載
    博途PLC1200/1500PLC用戶自<b class='flag-5'>定義</b><b class='flag-5'>數(shù)據(jù)類型</b>(UDT)

    西門子博途:使用PLC數(shù)據(jù)類型 (UDT)

    PLC 數(shù)據(jù)類型 (UDT) 是可自行定義且在程序中可以多次使用的數(shù)據(jù)結(jié)構(gòu)。 此結(jié)構(gòu)可包含不同數(shù)據(jù)類型的多個(gè)元素。 聲明 PLC 數(shù)據(jù)類型
    的頭像 發(fā)表于 07-12 17:36 ?9108次閱讀
    西門子博途:使用PLC<b class='flag-5'>數(shù)據(jù)類型</b> (UDT)

    淺談PLC定義數(shù)據(jù)類型的應(yīng)用

    PLC定義數(shù)據(jù)類型以下用一個(gè)例子介紹PLC定義數(shù)據(jù)類型的應(yīng)用,以便進(jìn)一步理解PLC定義數(shù)據(jù)類型
    的頭像 發(fā)表于 07-24 16:07 ?1524次閱讀
    淺談PLC<b class='flag-5'>定義</b><b class='flag-5'>數(shù)據(jù)類型</b>的應(yīng)用

    FreeRTOS使用的數(shù)據(jù)類型有哪些

    數(shù)據(jù)類型 FreeRTOS 使用的數(shù)據(jù)類型主要分為 stdint.h 文件中定義的和自己定義的。其中 char 和 char * 定義的變量
    的頭像 發(fā)表于 09-28 11:49 ?886次閱讀

    電子發(fā)燒友

    中國電子工程師最喜歡的網(wǎng)站

    • 2931785位工程師會(huì)員交流學(xué)習(xí)
    • 獲取您個(gè)性化的科技前沿技術(shù)信息
    • 參加活動(dòng)獲取豐厚的禮品