當(dāng)前位置:首頁 > 公眾號(hào)精選 > 嵌入式微處理器
[導(dǎo)讀]C和C++的最大區(qū)別便是,C++有類,C沒有類的概念。單單這一個(gè)類使得C缺失很多的東西。好在C有結(jié)構(gòu)體,勉強(qiáng)可以當(dāng)0.1個(gè)類來使用。

微信公眾號(hào):二進(jìn)制人生
專注于嵌入式linux開發(fā)。

目錄

前言1、繼承2、封裝偽構(gòu)造函數(shù)3、多態(tài)

前言

我們都知道C語言是一門過程性語言,所謂過程性就是在解決問題時(shí),將問題按步驟分解。

例如,做菜的時(shí)候,先點(diǎn)火,再倒油,接著下菜翻炒,最后加鹽和醬油。但有時(shí)候借鑒面向?qū)ο?/a>的思想來組織代碼,邏輯層次會(huì)更加清晰。

C和C++的最大區(qū)別便是,C++有類,C沒有類的概念。單單這一個(gè)類使得C缺失很多的東西。好在C有結(jié)構(gòu)體,勉強(qiáng)可以當(dāng)0.1個(gè)類來使用。

眾所周知,類有三大特性:封裝、繼承、多態(tài)。我們來看看C語言如何借鑒類的三大特性來更好的組織代碼。

1、繼承

C語言沒有嚴(yán)格意義上的繼承,可以借助結(jié)構(gòu)體嵌套實(shí)現(xiàn)類似于繼承的形式,但始終不盡人意。

struct?parent
{

????int?a;
};
struct?son
{

????struct?parent?p;//兒子繼承父親
????int?b;
};

C++的類可以實(shí)現(xiàn)成員的訪問控制,例如將變量b聲明成private,那么外部就無法訪問。但C的結(jié)構(gòu)體做不到。

在C++里頭,父親的私有成員,兒子是無法訪問的。結(jié)構(gòu)體嵌套也做不到。因?yàn)榻Y(jié)構(gòu)體根本就沒有訪問控制的概念。

對(duì)于C++而言,訪問控制實(shí)質(zhì)上是在編譯層做的,我們?nèi)耘f可以通過指針來間接訪問。

例如:

class?Base{
public:
???int?a;
private:
???int?b;
};

盡管b被聲明成私有,但我們?nèi)耘f有辦法訪問它(借助指針繞過語法檢查):

Base?t;
int?*p?=?&t.a;
cout?<1]?<endl;

2、封裝

封裝就是把數(shù)據(jù)和方法打包到一個(gè)類里面。C++的實(shí)現(xiàn)大致如下:

class?類名
{

public:
????公有方法1
????公有方法2
????……
????公有數(shù)據(jù)1
????……
private:
????私有方法1
????私有方法2
????……
????私有數(shù)據(jù)1
????……
};

這樣做的好處是顯而易見的。一個(gè)類實(shí)現(xiàn)了一個(gè)小模塊,使得代碼結(jié)構(gòu)比較清晰。對(duì)外接口和數(shù)據(jù)定義成public,允許調(diào)用者直接訪問。內(nèi)部接口和數(shù)據(jù)定義成private,外部不可見。

在 QT 中,為了更好的隱藏一個(gè)類的具體實(shí)現(xiàn),一般是一個(gè)公開頭文件、一個(gè)私有頭文件,私有頭文件中定義實(shí)現(xiàn)的內(nèi)部細(xì)節(jié),公開頭文件中定義開放給客戶程序員的接口和公共數(shù)據(jù)??纯?code style="font-size: inherit;line-height: inherit;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">QObject (qobject.h),對(duì)應(yīng)有一QObjectPrivate(qobject_p.h ) ,其他的也類似。

QObject{
public:
????xxx
????xxx
private:
????QObjectPrivate?*?priv;
};

我們可以借助C語言的指針和結(jié)構(gòu)體來實(shí)現(xiàn)方法和數(shù)據(jù)的封裝?;究蚣苋缦拢?/p>

struct?結(jié)構(gòu)體名{
????數(shù)據(jù)1
????數(shù)據(jù)2;
????……
????方法1:
????方法2;
????……
}

在結(jié)構(gòu)體里定義成員變量很容易,直接int a;
在結(jié)構(gòu)體里定義成員函數(shù)要使用函數(shù)指針,比如:

int?(*func)(void?*)

所以,我們把上面的框架具體化就是:

struct?manager{
????int?data1;
????int?data2;
????int?(*operation1)(struct?manager*);
????int?(*operation2)(struct?manager*);
};

實(shí)際上,C++的成員函數(shù)也是通過函數(shù)指針的形式來實(shí)現(xiàn),本質(zhì)上是一致的。我們都知道類的成員函數(shù)和類的成員變量是分開存儲(chǔ)的,同一個(gè)類的所有對(duì)象,成員函數(shù)只需要占據(jù)一份地址空間。

在定義結(jié)構(gòu)體之后,函數(shù)指針并沒有賦值,一般我們會(huì)定義一個(gè)結(jié)構(gòu)體初始化函數(shù)來初始化結(jié)構(gòu)體成員,這有點(diǎn)類似于類的構(gòu)造函數(shù),但類的構(gòu)造函數(shù)在創(chuàng)建對(duì)象時(shí)自動(dòng)調(diào)用,而我們這個(gè)結(jié)構(gòu)體初始化函數(shù)只能自己手動(dòng)調(diào)用了。

同樣的,對(duì)標(biāo)C++的析構(gòu)函數(shù),我們在C語言里頭有一個(gè)去初始化的函數(shù)來完成模塊的去初始化,這種思想不就是一樣的嗎?

static?int?operation1(struct?manager?*manager_ptr)
{
????……
}
static?int?operation2(struct?manager?*manager_ptr)
{
????……
}

偽構(gòu)造函數(shù)

注意,我們把兩個(gè)operation函數(shù)定義成了static,這樣子文件之外的函數(shù)就不能調(diào)用它,只能通過manager結(jié)構(gòu)體來調(diào)用。是不是感覺有點(diǎn)封裝的意味。

void?init_manager(struct?manager*?manager_ptr)
{
????manager_ptr->operation1?=?operation1;
????manager_ptr->operation2?=?operation2;
????manager_ptr->data1?=?0;
????manager_ptr->data2?=?0;
}

去初始化函數(shù)我就不寫了。

如果operation函數(shù)在外面的文件定義,則可以作為init_manager函數(shù)的參數(shù)傳入,這種場景也非常常見。我實(shí)現(xiàn)了模塊A,該模塊的operation1函數(shù)處理數(shù)據(jù)并輸出一些結(jié)果。但是我并不知道使用該模塊的人想要什么格式的結(jié)果,比如有一些人想要json格式的結(jié)果,有些人想要xml格式的結(jié)果。我不能幫他們一一實(shí)現(xiàn)一個(gè)方法,我干脆叫你們統(tǒng)一按照我指定的函數(shù)模板,實(shí)現(xiàn)一個(gè)處理函數(shù),完了你們調(diào)用結(jié)構(gòu)體初始化函數(shù)注冊下,我會(huì)在operation1函數(shù)處理完數(shù)據(jù)后,調(diào)用你們的處理函數(shù),給你們一個(gè)滿意的結(jié)果。

為了達(dá)到上面的目的,簡單修改下,我們把函數(shù)operation2定義成一種類型,

typedef?int??(*FUN_CBK)(struct?manager *manager_ptr);

結(jié)構(gòu)體定義稍作修改:

struct?manager{
????int?data1;
????int?data2;
????int?(*operation1)(struct?manager*);
????FUN_CBK?call_back;
};

結(jié)構(gòu)體初始化函數(shù)也要做相應(yīng)的修改,增加了一個(gè)函數(shù)指針形參:

void?init_manager(struct?manager*?manager_ptr,?\
FUN_CBK?fun)
{
????manager_ptr->operation1?=?operation1;
????manager_ptr->call_back?=?fun;//用外部傳入的回調(diào)函數(shù)進(jìn)行初始化
????manager_ptr->data1?=?0;
????manager_ptr->data2?=?0;
}

通過上面的操作,我們用結(jié)構(gòu)體和函數(shù)指針完成了模塊化封裝。

我看了網(wǎng)上的博客,有些人為了特意模仿類,還用以下方式實(shí)現(xiàn)了類似于類的構(gòu)造函數(shù):

struct?manager?*manager_create(int?m,?int?n)
{
????struct?manager?*m?=?(struct?manager?*)\
????malloc(sizeof(struct?manager?*));
????m->data1?=?m;
????m->data2?=?n;
????m->operation1?=?operation1;
????m->operation2?=?operation2;
}

以及類似于類的析構(gòu)函數(shù):

void?manager_delete(struct?manager?*m_ptr)
{
????free(m_ptr);
????m_ptr?=?NULL;
}

使用示例:

struct?manager?*m_ptr?=?manager_create?(1,2);
manager_delete(m_ptr);

個(gè)人不是很喜歡這種做法,萬一忘記調(diào)用manager_delete還有內(nèi)存泄露的風(fēng)險(xiǎn)。

結(jié)構(gòu)體歸根到底還是結(jié)構(gòu)體,不能實(shí)現(xiàn)成員對(duì)外不可見。而C++中將成員聲明成private之后,外部就無法訪問了。C語言里想這么做,只能將該成員移出結(jié)構(gòu)體,定義為static形式。因?yàn)镃不支持在結(jié)構(gòu)體內(nèi)部定義static變量(不信,你可以自己去試下)。

為何不能在結(jié)構(gòu)體內(nèi)定義static變量,想想就知道了,static變量的地址在編譯鏈接之后是唯一且確定的,而結(jié)構(gòu)體只有在實(shí)例化時(shí)才能確定其地址,并且每個(gè)結(jié)構(gòu)體實(shí)例都有自己的地址空間。

3、多態(tài)

多態(tài)在上面的例子也有體現(xiàn)。C語言實(shí)現(xiàn)的多態(tài)并非是嚴(yán)格意義上的多態(tài),但是這種思想的應(yīng)用很廣泛,我們姑且叫它多態(tài)吧。你不解C++的多態(tài)也沒關(guān)系,絲毫不影響你理解下文。

linux的VFS便借鑒了這種思想。VFS(Virtual File System)是內(nèi)核提供的文件系統(tǒng)抽象層,其提供了文件系統(tǒng)的操作接口,可以隱藏底層不同文件系統(tǒng)的實(shí)現(xiàn)。

一個(gè)文件系統(tǒng)無非就是實(shí)現(xiàn)對(duì)文件、目錄的管理。針對(duì)文件VFS定義了統(tǒng)一的結(jié)構(gòu)體:

struct?file?{
????union?{
????????struct?llist_nodefu_llist;
????????struct?rcu_head?fu_rcuhead;
????}?f_u;
????struct?pathf_path;
????struct?inode*f_inode;/*?cached?value?*/
????const?struct?file_operations*f_op;
????……
};?

strcut file代表一個(gè)文件,每種文件系統(tǒng)(比如ext3,vfat)實(shí)現(xiàn)讀寫等操作的方式都不一樣,所以將這些方法封裝成函數(shù)指針,統(tǒng)一定義在結(jié)構(gòu)體struct file_operations內(nèi)。

struct?file_operations?{
????struct?module?*owner;
????loff_t?(*llseek)?(struct?file?*,?loff_t,?int);
????ssize_t?(*read)?(struct?file?*,?char?__user?*,?size_t,?loff_t?*);
????ssize_t?(*write)?(struct?file?*,?const?char?__user?*,?size_t,?loff_t?*);
????……
};

每個(gè)文件系統(tǒng)各自完成自己的實(shí)現(xiàn)。


再寫一個(gè)實(shí)際的例子。
定義一個(gè)人的標(biāo)準(zhǔn)接口和數(shù)據(jù)如下:

strcut?man{
????int?head;
????int?body;
????……
????void?(*say_hello)(void);//見面問候的方式
};

中國人見面時(shí),說你好:

void?china_say_hello(void)
{
????printf(“你好”);
}

英國人見面時(shí),說hello:

void?english_say_hello(void)
{
????printf(“hello”);
}

現(xiàn)在來初始化它們各自的問候方式:

struct?man?china{
????.say_hello?=?china_say_hello;
}
struct?man?english{
????.say_hello?=?English_say_hello;
}

英國人和中國人對(duì)外呈現(xiàn)都是struct man,其見面問候的接口都是man.say_hello,但其底層實(shí)現(xiàn)卻可以不一樣。

并且我們可以在程序運(yùn)行時(shí),隨意的更改中國人的問候方式。比如嬰兒時(shí)期,只會(huì)“哇哇”叫,長大了才會(huì)說“你好”,我們可以改變成員say_hello的值,讓其在不同時(shí)期指向不同的函數(shù),從而達(dá)到運(yùn)行時(shí)多態(tài)的目的。

其實(shí)呢,C++的多態(tài),也是通過函數(shù)指針來實(shí)現(xiàn)的,學(xué)習(xí)過C++的同學(xué)就會(huì)知道,含有虛函數(shù)的類,會(huì)維護(hù)一個(gè)虛函數(shù)表,里面存放了虛函數(shù)的地址。所以說啊,C語言是C++的母語,萬變不離指針,指針是C語言的一大法寶。


-END-


本文授權(quán)轉(zhuǎn)載自二進(jìn)制人生,作者:二進(jìn)制人生




推薦閱讀



【01】長見識(shí):你真的知道C語言里extern "C" 的作用嗎?
【02】硬件工程師必知的10個(gè)C語言技巧
【03】C語言如何實(shí)現(xiàn)拷貝圖片?幾行代碼即可搞定
【04】C語言常用的一些轉(zhuǎn)換工具函數(shù)!
【05】2020年8月程序員工資最新統(tǒng)計(jì)


免責(zé)聲明:整理文章為傳播相關(guān)技術(shù),版權(quán)歸原作者所有,如有侵權(quán),請(qǐng)聯(lián)系刪除

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場,如有問題,請(qǐng)聯(lián)系我們,謝謝!

嵌入式ARM

掃描二維碼,關(guān)注更多精彩內(nèi)容

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動(dòng)力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉