最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

用C語言實(shí)現(xiàn)面向?qū)ο?第二版)

2021-07-25 06:55 作者:陳偉國AE  | 我要投稿

上個(gè)月閑的無聊湊空閑時(shí)間寫了個(gè)C語言面向?qū)ο?原文鏈接:

https://www.bilibili.com/read/cv11292041

專欄發(fā)布后自己也重新看了下,功能很不完善,重新看了遍代碼發(fā)現(xiàn)還有很多BUG。由于暑假時(shí)間比較多,就重新把這框架修了一遍,修復(fù)了一些BUG以及更新了很多實(shí)用的功能模塊。

下面進(jìn)入正題,本文包括三個(gè)部分,基本功能模塊、實(shí)用功能模塊介紹以及功能演示

1. 基本功能模塊:

(1) 宏函數(shù)new()的實(shí)現(xiàn)

(2) this指針介紹

(3) 對象調(diào)用宏函數(shù)$()的實(shí)現(xiàn)

(4) 宏函數(shù)instanceof()的實(shí)現(xiàn)

(5) 創(chuàng)建自己的類

(6) GC實(shí)現(xiàn)

(7) 宏函數(shù)GC_INIT()介紹

2. 實(shí)用功能模塊:

(1) List對象的使用

(2) Map對象的使用

(3) String對象的使用

(4) JsonObject對象的使用


基本功能模塊

????該部分介紹了面向?qū)ο笙嚓P(guān)功能基本實(shí)現(xiàn)思路,以及如何使用,使用時(shí)需要注意哪些事項(xiàng)。

?(1) 宏函數(shù)new()

使用new()創(chuàng)建一個(gè)對象

????上面的代碼中通過調(diào)用new()創(chuàng)建了一個(gè)List對象,調(diào)用過程中執(zhí)行了以下幾個(gè)步驟:

????????1.內(nèi)存分配,使用malloc()函數(shù)在堆區(qū)分配空間

? ? ????2.計(jì)算內(nèi)存對齊,GC提示創(chuàng)建對象,分配了多少字節(jié)空間

????????3.更新對象地址范圍,更新已分配的堆內(nèi)存地址范圍

????????4.調(diào)用該類構(gòu)造函數(shù)

????????5.將this指針設(shè)置為當(dāng)前

?代碼實(shí)現(xiàn):

new()實(shí)現(xiàn)

????上面第3步需要解釋下,對象地址范圍和堆內(nèi)存地址范圍的作用。眾所周知在C語言中,free()函數(shù)如果傳入的指針不是堆區(qū)內(nèi)存指針,free()函數(shù)就會報(bào)錯(cuò)。為了解決這個(gè)問題,在free()之前必須判斷傳入的指針是不是堆區(qū)的指針,通過定義堆內(nèi)存地址范圍,每次調(diào)用malloc()時(shí)記錄下分配過的最大地址和最小地址,那么只要是在這個(gè)范圍內(nèi)的,一定就是堆區(qū)內(nèi)存指針。由于堆區(qū)內(nèi)存指針有兩種情況:對象和數(shù)據(jù),所以使用對象地址范圍來判斷該指針是不是對象。


?(2) this指針

this指針的使用
使用String對象調(diào)用equals()函數(shù)

????上面的代碼是String類的equals()函數(shù)實(shí)現(xiàn),this指針用于在函數(shù)中訪問當(dāng)前引用的對象,如果不使用this指針的話,在使用對象調(diào)用equals()函數(shù)時(shí)就必須將引用作為參數(shù)傳進(jìn)去,手感很差,比如:$(s).equals(s,"hello"); 這樣寫起來十分不爽。

????注意下上面代碼中的String對象并沒有使用new()創(chuàng)建,是因?yàn)槲覟榱藘?yōu)化手感,在String對象的創(chuàng)建上參照了《JVAV》,可以直接將字符串指針賦值給String對象,在對象調(diào)用時(shí)會自動創(chuàng)建String對象,并賦初值,下面在講對象調(diào)用宏函數(shù)$()時(shí)會詳細(xì)說明。

關(guān)于this指針的線程安全問題,因?yàn)閠his指針是共享的全局變量,使用不當(dāng)會導(dǎo)致線程安全問題首先GC線程里所有的對象引用都是不使用this指針的,所以GC線程不會影響this指針,關(guān)于實(shí)際使用時(shí)產(chǎn)生的線程安全問題,需要注意:

????在函數(shù)實(shí)現(xiàn)時(shí),函數(shù)中的第一條語句必須是Object temp=(Object) this;用于在??臻g中保存當(dāng)前this指針,后續(xù)調(diào)用就使用temp對象。


(3) ?對象調(diào)用宏函數(shù)$()

使用$(n)調(diào)用對象(1)
使用$(n)調(diào)用對象(2)

上面是對象調(diào)用的兩種方式,其實(shí)本質(zhì)上都是調(diào)用宏函數(shù) $(n) 去引用對象,不過第二種方式打起來更加方便,如果一個(gè)對象使用頻率比較高,推薦使用第二種方式。第一種方式 $(n).func();類似于jQuery,手感還行。


$(n)代碼實(shí)現(xiàn):

宏函數(shù)$(n)代碼實(shí)現(xiàn)

上面是對象調(diào)用宏函數(shù)$(n)的實(shí)現(xiàn),調(diào)用過程中執(zhí)行了以下幾個(gè)步驟:

? ? 1.將this指針指向當(dāng)前對象

????2.如果當(dāng)前對象不是對象而是字符串指針,則將其轉(zhuǎn)換為String對象

????3.當(dāng)前對象計(jì)數(shù)值清零

????4.當(dāng)前對象引用次數(shù)加一

其中第3和第4步與GC相關(guān),由于GC根據(jù)超時(shí)時(shí)間和引用次數(shù)來回收對象,所以當(dāng)對象產(chǎn)生引用時(shí),需要將計(jì)數(shù)值清零,防止被GC回收


(4)??宏函數(shù)instanceof()的實(shí)現(xiàn)

宏函數(shù)instanceof(n)的使用

這里的宏函數(shù)instanceof()是仿照《JVAV》中的instanceof關(guān)鍵字實(shí)現(xiàn)的,功能與其一致,它的作用是測試它左邊的對象是否是它右邊的類的實(shí)例,返回 boolean 的數(shù)據(jù)類型。

代碼實(shí)現(xiàn):

實(shí)現(xiàn)原理十分簡單,由于每個(gè)對象有一個(gè)基類Object,instanceof()通過將其強(qiáng)轉(zhuǎn)為Object對象后,獲取它的attr.type屬性即可知道該對象是哪個(gè)類的實(shí)例。

注意上圖中的$String定義在DataType.h中,每一個(gè)類都必須定義用于判斷對象類型,例如:

#define $JsonObject ? ? 9

下面是兩個(gè)與instanceof()功能相近的函數(shù):

GetObjectType(void* obj);

GetObjectTypeName(void* obj);

獲取對象類型函數(shù)



(5) 創(chuàng)建自己的類

TEST類結(jié)構(gòu)

上圖為類的基本結(jié)構(gòu),包括結(jié)構(gòu)體,結(jié)構(gòu)體指針,有參構(gòu)造函數(shù)無參構(gòu)造函數(shù)

  1. 結(jié)構(gòu)體:名字必須是下劃線+類名,在new()調(diào)用時(shí)會根據(jù)這個(gè)名字創(chuàng)建對應(yīng)的臨時(shí)變量。結(jié)構(gòu)體中第一個(gè)元素必須是基類Object對象,用于強(qiáng)制類型轉(zhuǎn)換和基本對象屬性的存儲。最后是你自己的屬性和函數(shù)。

  2. 結(jié)構(gòu)體指針:名字必須是類名,用于存儲當(dāng)前對象

  3. 有參構(gòu)造函數(shù)和無參構(gòu)造函數(shù):名字必須是Const+下劃線+類名+(ARGS), 在new()時(shí)會根據(jù)名字來調(diào)用對應(yīng)的構(gòu)造函數(shù),兩個(gè)構(gòu)造函數(shù)必須都要聲明,否則new()中將會找不到構(gòu)造函數(shù)導(dǎo)致異常。

TEST構(gòu)造函數(shù)內(nèi)容

在構(gòu)造函數(shù)中,必須調(diào)用Object_init(void*,int,boolean);對基類Object對象進(jìn)行初始化。第一個(gè)參數(shù)是當(dāng)前對象,第二個(gè)參數(shù)是當(dāng)前對象類型,第三個(gè)參數(shù)是是否可以被GC回收。其次,在構(gòu)造函數(shù)中還需要對函數(shù)指針進(jìn)行賦值,否則函數(shù)將無法被正確調(diào)用。

關(guān)于類的創(chuàng)建只有這么多,十分簡單,上圖的TEST類就是一個(gè)模板,直接復(fù)制后把參數(shù)名修改修改就能用了。


(6) GC實(shí)現(xiàn)

關(guān)于GC,我放棄了之前的直接使用計(jì)數(shù)法實(shí)現(xiàn)方式,轉(zhuǎn)而采用引用次數(shù)權(quán)重+引用周期權(quán)重+優(yōu)先級的方法實(shí)現(xiàn)。再次簡述下基本實(shí)現(xiàn)原理。

在系統(tǒng)運(yùn)行的過程中,對象的引用可以大體分為一下三類:

  1. 對象被高頻周期性引用,如定時(shí)器,while(1)循環(huán)中調(diào)用等

  2. 對象創(chuàng)建時(shí)高頻引用,然后再也不引用,只有偶爾會產(chǎn)生幾次引用,如中斷事件,標(biāo)志位觸發(fā)等

  3. 對象引用周期長的周期性引用,大部分時(shí)間在等待,只有很少時(shí)間被調(diào)用

由于有上述三種情況,單純的采用計(jì)數(shù)法會導(dǎo)致長引用周期的對象優(yōu)先級低,容易被誤回收,偶爾產(chǎn)生引用的對象更容易被回收,因此采用計(jì)數(shù)法實(shí)現(xiàn)是不科學(xué)的。為了解決這個(gè)問題,我必須動態(tài)的去適配每個(gè)對象的引用周期,如果采用固定超時(shí)時(shí)間,一旦對象超時(shí)就會被回收,這樣誤回收的概率極高,為了簡化問題,我將上述三種情況融合為一種情況。

在系統(tǒng)開機(jī)后,對象被創(chuàng)建出來,并高頻調(diào)用其函數(shù)和屬性以初始化系統(tǒng),初始化結(jié)束后,對象開始進(jìn)入周期性引用,周期性引用持續(xù)了1天后,對象的引用開始進(jìn)入隨機(jī)觸發(fā),非周期性調(diào)用。這種情況下,要動態(tài)的適配對象在不同環(huán)境下的引用周期,計(jì)數(shù)法就不再適用了。由于對象一開始會產(chǎn)生高頻調(diào)用,說明該階段的引用周期短,引用次數(shù)多,總引用次數(shù)會被拉的很高,如果采用平均值引用周期的話,第二階段的周期性引用就很難把周期值拉大,也就是易受到高頻影響。此時(shí)采用權(quán)重法,記錄下平均引用周期和最大引用周期值,根據(jù)總引用次數(shù)在平均周期下需要執(zhí)行的的次數(shù)與最大周期下需要執(zhí)行的次數(shù)的占比來控制輸出的引用周期。計(jì)算出權(quán)重,比值越大說明周期接近平均值居多,反之周期長且頻率低居多,這樣產(chǎn)生的結(jié)果就能動態(tài)的跟隨趨勢,趨向于短周期時(shí)間或長周期時(shí)間,在周期時(shí)間不斷變化時(shí),產(chǎn)生的權(quán)重也會隨著趨勢動態(tài)的更新。

引用周期簡圖


定義A?為在一段時(shí)間T內(nèi),用最短周期可以產(chǎn)生多少次調(diào)用

定義B為 在一段時(shí)間T內(nèi),用最長周期可以產(chǎn)生多少次調(diào)用

A由于周期短,所以頻率高,A的值越大

B由于周期長,所以頻率低,B的值越小

同理,實(shí)際調(diào)用次數(shù)C如果越大,則說明短周期占比高,反之長周期占比高

如果C在(A+B)中的占比越大,說明這一段時(shí)間里對象是被高頻引用的,則A的權(quán)重越大

如果C在(A+B)中的占比越小,說明這一段時(shí)間里對象是被低頻引用的,則B的權(quán)重越大

例如一段時(shí)間T內(nèi)實(shí)際調(diào)用次數(shù)為30,A為100,B為3,則占比為30/103=0.291,說明短周期調(diào)用可能是突發(fā)的,隨機(jī)的,而非周期性的

例如一段時(shí)間T內(nèi)實(shí)際調(diào)用次數(shù)為300,A為350,B為3,則占比為300/353=0.849,說明短周期調(diào)用很可能是周期性的,而長周期調(diào)用可能是delay()等函數(shù)后產(chǎn)生的調(diào)用

其中A,B,C,T都是隨時(shí)間不斷累積的,最長和最短周期也會動態(tài)更新,隨著總時(shí)間的不斷加長,實(shí)際調(diào)用次數(shù)和A+B之間的占比就越精確。

假設(shè)系統(tǒng)運(yùn)行了3600秒,總調(diào)用次數(shù)100,A為3600,B為60,則權(quán)重為0.027

假設(shè)系統(tǒng)運(yùn)行了3600000秒,總調(diào)用次數(shù)100,A為3600000,B為60000,則權(quán)重為0.000027


目前GC再加上語法檢測功能就完善了,根據(jù)C11新特性的__FILE__和__LINE__屬性獲取文件名和行數(shù),如果同文件同一行存在同一類型的new()創(chuàng)建對象的話,說明很可能是在循環(huán)中調(diào)用的new(),如果循環(huán)跳到下一輪,且上一輪的對象沒有存在容器中,則上一輪的對象將會失去引用,而語法檢測就是需要規(guī)避這一點(diǎn),直接清除上一輪對象。由于時(shí)間緊張,就得放到3.0實(shí)現(xiàn)了。


GC代碼實(shí)現(xiàn):

引用周期權(quán)重計(jì)算和引用次數(shù)權(quán)重計(jì)算
優(yōu)先級計(jì)算


當(dāng)然GC如果無法滿足需求的話,可以選擇不用,我還提供了手動內(nèi)存管理函數(shù):


下面是GC引用周期調(diào)試視頻:

可以看到在第11秒第一次按下回車觸發(fā)對象一次調(diào)用時(shí),對象的最長引用周期被更新為11秒,第二次按下回車觸發(fā)對象200ms周期循環(huán)調(diào)用時(shí),對象的引用周期逐漸趨近于實(shí)際周期值200ms


(7) 宏函數(shù)GC_INIT()介紹

宏函數(shù)GC_INIT()實(shí)現(xiàn)

上圖為宏函數(shù)GC_INIT()實(shí)現(xiàn),初始化過程中主要執(zhí)行如下幾個(gè)功能:

  1. 獲取常量區(qū)起始地址,用于識別常量字符串指針

  2. 計(jì)算內(nèi)存對齊系數(shù)和最小分配內(nèi)存大小

  3. 適配stm32f1的內(nèi)存對齊系數(shù)

  4. 開啟GC線程

注意GC_INIT()必須放在main()函數(shù)開頭位置,以正確獲取常量區(qū)起始地址和堆內(nèi)存起始地址。



實(shí)用功能模塊

?(1) List對象的使用

List對象的使用

這里的List對象時(shí)仿照J(rèn)VAV中的List實(shí)現(xiàn)的,可以看到除了基本的增刪功能以外,該List的存儲類型是任意類型,圖中的List中分別存放了char*,int和JsonObject三種不同類型,但其本質(zhì)上都是void*,由于該框架支持指針類型識別,所以在調(diào)用$(l1).print()時(shí)可以根據(jù)數(shù)據(jù)的真實(shí)類型輸出結(jié)果,List中其他函數(shù)功能與名字均與JVAV中一樣。


(2)?Map對象的使用

Map對象的使用

可以看到Map與List一樣,都是仿照J(rèn)VAV的格式實(shí)現(xiàn)的,除了基本的增刪功能以外,Map的數(shù)據(jù)存儲類型是char*->void*,也就是Key是字符串,Value是任意類型,與List一樣,可以自動識別對象類型后處理。上圖為使用Map對象展示一個(gè)簡單的Map嵌套。


(3) String對象的使用

String對象創(chuàng)建方法

上圖是String對象的兩種創(chuàng)建方法,第一種是直接將字符串指針賦值給String對象,第二種則是采用祖?zhèn)鞯膎ew()法進(jìn)行對象創(chuàng)建。這兩種方法都能正常創(chuàng)建String對象,不同的是,第一種對象在引用前是沒有創(chuàng)建出String對象的(沒有調(diào)用new()函數(shù)),第二種則是直接調(diào)用new()創(chuàng)建。不過這兩種方式創(chuàng)建的String對象在使用上沒有任何區(qū)別,推薦使用第一種方式。


下面是String類中幾個(gè)實(shí)用的函數(shù):

乍一看還以為在打JVAV,沒錯(cuò),String的也是仿照J(rèn)VAV的String實(shí)現(xiàn)的。下面就演示幾個(gè)核心功能,如圖:

split()函數(shù)的使用
substring()函數(shù)的使用
indexof()和contains()的使用


(4) JsonObject的使用

首先說明只實(shí)現(xiàn)了JsonObject,沒有實(shí)現(xiàn)JsonArray,由于時(shí)間緊還得忙別的事,此功能和GC語法檢測,以及性能優(yōu)化,內(nèi)存池等功能就得放在3.0版本了。先看效果圖吧。

JsonObject對象的使用

運(yùn)行結(jié)果:

toJsonString()結(jié)果
toJsonObject()結(jié)果
json驗(yàn)證結(jié)果

上面代碼中使用了游戲背包結(jié)構(gòu)做了一個(gè)簡單的測試,可以正常生成Json數(shù)據(jù),Json數(shù)據(jù)的存儲格式也是字符串:任意數(shù)據(jù)類型,可以自動識別數(shù)據(jù)類型后生成對應(yīng)的Json字符串,可以實(shí)現(xiàn)嵌套Json對象的Json字符串生成以及解析。

寫不動了,已經(jīng)從凌晨1點(diǎn)寫到7點(diǎn)了,中途4點(diǎn)的時(shí)候被b站專欄坑了,沒保存成功重寫到現(xiàn)在。

總結(jié)一下,大家千萬不要閑的沒事干去用C語言仿照J(rèn)VAV實(shí)現(xiàn)面向?qū)ο?C++他不香嗎,開了一個(gè)坑真的流著淚都得填完,千萬別亂入坑。



工程代碼像以往一樣直接放出來:

https://pan.baidu.com/s/1t7_bECM5CIJdBwE0a60Dcw

提取碼:ALYA

Clion打開別人的工程會導(dǎo)致路徑報(bào)錯(cuò),參考我Clion開發(fā)Stm32這篇最后的教程就行https://www.bilibili.com/read/cv11442303

用C語言實(shí)現(xiàn)面向?qū)ο?第二版)的評論 (共 條)

分享到微博請遵守國家法律
泸西县| 广水市| 徐水县| 宣化县| 康马县| 永平县| 张北县| 灵山县| 林周县| 关岭| 陇川县| 东辽县| 田阳县| 育儿| 阿巴嘎旗| 吉林省| 连城县| 娄底市| 苍溪县| 弥渡县| 景泰县| 定西市| 乌海市| 手游| 章丘市| 固原市| 涞源县| 牙克石市| 瓦房店市| 滨海县| 江门市| 文登市| 虎林市| 林芝县| 南宁市| 肥城市| 嘉黎县| 和顺县| 易门县| 松溪县| 疏勒县|