国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

C語言要如何面向對象編程?

Q4MP_gh_c472c21 ? 來源:IoT物聯網小鎮 ? 作者:道哥 ? 2021-01-26 09:58 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

一、前言

嵌入式開發中,C/C++語言是使用最普及的,在C++11版本之前,它們的語法是比較相似的,只不過C++提供了面向對象的編程方式。

雖然C++語言是從C語言發展而來的,但是今天的C++已經不是當年的C語言的擴展了,從2011版本開始,更像是一門全新的語言。

那么沒有想過,當初為什么要擴展出C++?C語言有什么樣的缺點導致C++的產生?

2baa71cc-5f63-11eb-8b86-12bb97331649.png

C++在這幾個問題上的解決的確很好,但是隨著語言標準的逐步擴充,C++語言的學習難度也逐漸加大。沒有開發過幾個項目,都不好意思說自己學會了C++,那些左值、右值、模板、模板參數、可變模板參數等等一堆的概念,真的不是使用2,3年就可以熟練掌握的。

但是,C語言也有很多的優點:

2bd20d40-5f63-11eb-8b86-12bb97331649.png

其實最后一個優點是最重要的:使用的人越多,生命力就越強。就像現在的社會一樣,不是優者生存,而是適者生存。

這篇文章,我們就來聊聊如何在C語言中利用面向對象的思想來編程。也許你在項目中用不到,但是也強烈建議你看一下,因為我之前在跳槽的時候就兩次被問到這個問題。

二、什么是面向對象編程

有這么一個公式:程序=數據結構+算法

C語言中一般使用面向過程編程,就是分析出解決問題所需要的步驟,然后用函數把這些步驟一步一步調用,在函數中對數據結構進行處理(執行算法),也就是說數據結構和算法是分開的。

C++語言把數據和算法封裝在一起,形成一個整體,無論是對它的屬性進行操作、還是對它的行為進行調用,都是通過一個對象來執行,這就是面向對象編程思想。

如果用C語言來模擬這樣的編程方式,需要解決3個問題:

數據的封裝

繼承

多態

第一個問題:封裝

封裝描述的是數據的組織形式,就是把屬于一個對象的所有屬性(數據)組織在一起,C語言中的結構體類型天生就支持這一點。

第二個問題:繼承

繼承描述的是對象之間的關系,子類通過繼承父類,自動擁有父類中的屬性和行為(也就是方法)。這個問題只要理解了C語言的內存模型,也不是問題,只要在子類結構體中的第一個成員變量的位置放置一個父類結構體變量,那么子類對象就繼承了父類中的屬性。

另外補充一點:學習任何一種語言,一定要理解內存模型!

第三個問題:多態

按字面理解,多態就是“多種狀態”,描述的是一種動態的行為。在C++中,只有通過基類引用或者指針,去調用虛函數的時候才發生多態,也就是說多態是發生在運行期間的,C++內部通過一個虛表來實現多態。那么在C語言中,我們也可以按照這個思路來實現。

如果一門語言只支持類,而不支持多態,只能說它是基于對象的,而不是面向對象的。

既然思路上沒有問題,那么我們就來簡單的實現一個。

三、先實現一個父類,解決封裝的問題

Animal.h

#ifndef _ANIMAL_H_#define _ANIMAL_H_ // 定義父類結構typedef struct { int age; int weight;} Animal; // 構造函數聲明void Animal_Ctor(Animal *this, int age, int weight); // 獲取父類屬性聲明int Animal_GetAge(Animal *this);int Animal_GetWeight(Animal *this); #endifAnimal.c

#include “Animal.h” // 父類構造函數實現void Animal_Ctor(Animal *this, int age, int weight){ this-》age = age; this-》weight = weight;} int Animal_GetAge(Animal *this){ return this-》age;} int Animal_GetWeight(Animal *this){ return this-》weight;}

測試一下:

#include 《stdio.h》#include “Animal.h”#include “Dog.h” int main(){ // 在棧上創建一個對象 Animal a; // 構造對象 Animal_Ctor(&a, 1, 3); printf(“age = %d, weight = %d ”, Animal_GetAge(&a), Animal_GetWeight(&a)); return 0;}

可以簡單的理解為:在代碼段有一塊空間,存儲著可以處理Animal對象的函數;在棧中有一塊空間,存儲著a對象。

2d650680-5f63-11eb-8b86-12bb97331649.png

與C++對比:在C++的方法中,隱含著第一個參數this指針。當調用一個對象的方法時,編譯器會自動把對象的地址傳遞給這個指針。

所以,在Animal.h中函數我們就模擬一下,顯示的定義這個this指針,在調用時主動把對象的地址傳遞給它,這樣的話,函數就可以對任意一個Animal對象進行處理了。

四、 實現一個子類,解決繼承的問題

Dog.h

#ifndef _DOG_H_#define _DOG_H_ #include “Animal.h” // 定義子類結構typedef struct { Animal parent; // 第一個位置放置父類結構 int legs; // 添加子類自己的屬性}Dog; // 子類構造函數聲明void Dog_Ctor(Dog *this, int age, int weight, int legs); // 子類屬性聲明int Dog_GetAge(Dog *this);int Dog_GetWeight(Dog *this);int Dog_GetLegs(Dog *this); #endif

Dog.c

#include “Dog.h” // 子類構造函數實現void Dog_Ctor(Dog *this, int age, int weight, int legs){ // 首先調用父類構造函數,來初始化從父類繼承的數據 Animal_Ctor(&this-》parent, age, weight); // 然后初始化子類自己的數據 this-》legs = legs;} int Dog_GetAge(Dog *this){ // age屬性是繼承而來,轉發給父類中的獲取屬性函數 return Animal_GetAge(&this-》parent);} int Dog_GetWeight(Dog *this){ return Animal_GetWeight(&this-》parent);} int Dog_GetLegs(Dog *this){ // 子類自己的屬性,直接返回 return this-》legs;}

測試一下:

int main(){ Dog d; Dog_Ctor(&d, 1, 3, 4); printf(“age = %d, weight = %d, legs = %d ”, Dog_GetAge(&d), Dog_GetWeight(&d), Dog_GetLegs(&d)); return 0;}

在代碼段有一塊空間,存儲著可以處理Dog對象的函數;在棧中有一塊空間,存儲著d對象。由于Dog結構體中的第一個參數是Animal對象,所以從內存模型上看,子類就包含了父類中定義的屬性。

2da251de-5f63-11eb-8b86-12bb97331649.png

Dog的內存模型中開頭部分就自動包括了Animal中的成員,也即是說Dog繼承了Animal的屬性。

五、利用虛函數,解決多態問題

在C++中,如果一個父類中定義了虛函數,那么編譯器就會在這個內存中開辟一塊空間放置虛表,這張表里的每一個item都是一個函數指針,然后在父類的內存模型中放一個虛表指針,指向上面這個虛表。

上面這段描述不是十分準確,主要看各家編譯器的處理方式,不過大部分C++處理器都是這么干的,我們可以想這么理解。

子類在繼承父類之后,在內存中又會開辟一塊空間來放置子類自己的虛表,然后讓繼承而來的虛表指針指向子類自己的虛表。

2e251fe2-5f63-11eb-8b86-12bb97331649.png

既然C++是這么做的,那我們就用C來手動模擬這個行為:創建虛表和虛表指針。

1. Animal.h為父類Animal中,添加虛表和虛表指針

#ifndef _ANIMAL_H_#define _ANIMAL_H_ struct AnimalVTable; // 父類虛表的前置聲明 // 父類結構typedef struct { struct AnimalVTable *vptr; // 虛表指針 int age; int weight;} Animal; // 父類中的虛表struct AnimalVTable{ void (*say)(Animal *this); // 虛函數指針}; // 父類中實現的虛函數void Animal_Say(Animal *this); #endif

2. Animal.c

#include 《assert.h》#include “Animal.h” // 父類中虛函數的具體實現static void _Animal_Say(Animal *this){ // 因為父類Animal是一個抽象的東西,不應該被實例化。 // 父類中的這個虛函數不應該被調用,也就是說子類必須實現這個虛函數。 // 類似于C++中的純虛函數。 assert(0); } // 父類構造函數void Animal_Ctor(Animal *this, int age, int weight){ // 首先定義一個虛表 static struct AnimalVTable animal_vtbl = {_Animal_Say}; // 讓虛表指針指向上面這個虛表 this-》vptr = &animal_vtbl; this-》age = age; this-》weight = weight;} // 測試多態:傳入的參數類型是父類指針void Animal_Say(Animal *this){ // 如果this實際指向一個子類Dog對象,那么this-》vptr這個虛表指針指向子類自己的虛表, // 因此,this-》vptr-》say將會調用子類虛表中的函數。 this-》vptr-》say(this);}

2e64ec44-5f63-11eb-8b86-12bb97331649.png

在棧空間定義了一個虛函數表animal_vtbl,這個表中的每一項都是一個函數指針,例如:函數指針say就指向了代碼段中的函數_Animal_Say()。 》 對象a的第一個成員vptr是一個指針,指向了這個虛函數表animal_vtbl。

3. Dog.h不變

4. Dog.c中定義子類自己的虛表

#include “Dog.h” // 子類中虛函數的具體實現static void _Dog_Say(Dog *this){ printf(“dag say ”);} // 子類構造函數void Dog_Ctor(Dog *this, int age, int weight, int legs){ // 首先調用父類構造函數。 Animal_Ctor(&this-》parent, age, weight); // 定義子類自己的虛函數表 static struct AnimalVTable dog_vtbl = {_Dog_Say}; // 把從父類中繼承得到的虛表指針指向子類自己的虛表 this-》parent.vptr = &dog_vtbl; // 初始化子類自己的屬性 this-》legs = legs;}

5. 測試一下

int main(){ // 在棧中創建一個子類Dog對象 Dog d; Dog_Ctor(&d, 1, 3, 4); // 把子類對象賦值給父類指針 Animal *pa = &d; // 傳遞父類指針,將會調用子類中實現的虛函數。 Animal_Say(pa);}

內存模型如下:

2eba9220-5f63-11eb-8b86-12bb97331649.png

對象d中,從父類繼承而來的虛表指針vptr,所指向的虛表是dog_vtbl。

在執行Animal_Say(pa)的時候,雖然參數類型是指向父類Animal的指針,但是實際傳入的pa是一個指向子類Dog的對象,這個對象中的虛表指針vptr指向的是子類中自己定義的虛表dog_vtbl,這個虛表中的函數指針say指向的是子類中重新定義的虛函數_Dog_Say,因此this-》vptr-》say(this)最終調用的函數就是_Dog_Say。

基本上,在C中面向對象的開發思想就是以上這樣。這個代碼很簡單,自己手敲一下就可以了。如果想偷懶,請在后臺留言,我發給您。

六、C面向對象思想在項目中的使用

1. Linux內核

看一下關于socket的幾個結構體:

struct sock { 。..} struct inet_sock { struct sock sk; 。..}; struct udp_sock { struct sock sk; 。..};

2ee7efc2-5f63-11eb-8b86-12bb97331649.png

sock可以看作是父類,inet_sock和udp_sock的第一個成員都是是sock類型,從內存模型上看相當于是繼承了sock中的所有屬性。

2. glib庫

以最簡單的字符串處理函數來舉例:

GString *g_string_truncate(GString *string, gint len)GString *g_string_append(GString *string, gchar *val)GString *g_string_prepend(GString *string, gchar *val)

API函數的第一個參數都是一個GString對象指針,指向需要處理的那個字符串對象。

GString *s1, *s2;s1 = g_string_new(“Hello”);s2 = g_string_new(“Hello”); g_string_append(s1,“ World!”);g_string_append(s2,“ World!”);

3. 其他項目

還有一些項目,雖然從函數的參數上來看,似乎不是面向對象的,但是在數據結構的設計上看來,也是面向對象的思想,比如:

1. Modbus協議的開源庫libmodbus

2. 用于家庭自動化的無線通訊協議ZWave

3. 很久之前的高通手機開發平臺BREW

原文標題:一步步分析:C語言如何面向對象編程

文章出處:【微信公眾號:嵌入式ARM】歡迎添加關注!文章轉載請注明出處。

責任編輯:haq

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 嵌入式
    +關注

    關注

    5198

    文章

    20442

    瀏覽量

    333964
  • C語言
    +關注

    關注

    183

    文章

    7644

    瀏覽量

    145565
  • 編程
    +關注

    關注

    90

    文章

    3716

    瀏覽量

    97178

原文標題:一步步分析:C語言如何面向對象編程

文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    C語言增量式PID的通用算法

    C語言增量式PID通用算法控制算法中PID使用的非常廣泛,但是在網上找代碼的時候發現好多代碼都不夠通用,需要自己改好多東西,而且當一個項目需要使用多個PID控制器時也頗為麻煩,這里設計一個通用的增量
    發表于 01-14 08:28

    汽車網絡安全開發語言選型指南:C/C++/Rust/Java等主流語言對比+Perforce QAC/Klocwork工具支持

    汽車網絡安全如何選編程語言CC++、Rust、Java……誰更適合AUTOSAR、ISO/SAE 21434?一文了解8種主流語言的優劣
    的頭像 發表于 12-26 11:13 ?423次閱讀
    汽車網絡安全開發<b class='flag-5'>語言</b>選型指南:<b class='flag-5'>C</b>/<b class='flag-5'>C</b>++/Rust/Java等主流<b class='flag-5'>語言</b>對比+Perforce QAC/Klocwork工具支持

    C語言C++的區別及聯系

    class等面向對象的特性和機制。但是,后來經過一步步修訂和很多次演變,最終才形成了現如今這個支持一系列重大特性的龐大編程語言。 一、C
    發表于 12-24 07:23

    CC++之間的聯系

    ,后來才逐漸演變為一種成熟的面向對象編程語言。 總之,C語言
    發表于 12-11 06:51

    C語言C++之間的區別是什么

    區別 1、面向對象編程 (OOP): C語言是一種面向過程的
    發表于 12-11 06:23

    單片機C語言編程的心得

    寫這個8*8按鍵程序的過程中,不管是在自己寫還是參考別人程序的過程中,發現自己對C語言有些基本知識點和編程規范有很多不懂的地方,有些是自己以前的編程習慣不好,有些就是基礎知識不扎實的表
    發表于 12-08 07:44

    如何調試和編程CW32F030C8T7?支持哪些調試工具和編程語言

    了解如何調試和編程CW32F030C8T7是開發過程中的重要環節。它支持哪些調試工具(如JTAG、串口調試等)和編程語言(如C
    發表于 12-05 06:48

    為什么單片機還在用C語言編程

    說起單片機我們就會想到C語言,單片機為什么還在用C語言編程?現在有很多很好用的高級語言,如VC、
    發表于 11-28 07:37

    C語言編程技巧

    設計,分層開發代碼,便于理解和維護。 ?8、避免使用GOTO語句?:盡管GOTO語句在某些情況下仍然有用,但現代編程更傾向于使用函數和循環控制結構,以提高代碼的可讀性和維護性。 ?9、利用C語言的靈活性
    發表于 11-27 06:46

    C語言和單片機C語言有什么差異

    區別是由編譯器決的,只能參考對應的編譯手冊,即使同種平臺不同的編譯器對C的擴展也有不同。 3、單片機c語言編程是基于C
    發表于 11-14 07:55

    單片機c語言編程實例大全

    單片機c語言編程實例大全_18
    發表于 04-30 16:11 ?7次下載

    深入理解C語言C語言循環控制

    C語言編程中,循環結構是至關重要的,它可以讓程序重復執行特定的代碼塊,從而提高編程效率。然而,為了避免程序進入無限循環,C
    的頭像 發表于 04-29 18:49 ?2034次閱讀
    深入理解<b class='flag-5'>C</b><b class='flag-5'>語言</b>:<b class='flag-5'>C</b><b class='flag-5'>語言</b>循環控制

    如何在 樹莓派 上編寫和運行 C 語言程序?

    在本教程中,我將討論C編程語言是什么,C編程的用途,以及如何在RaspberryPi上編寫和運行C
    的頭像 發表于 03-25 09:28 ?1155次閱讀
    如何在 樹莓派 上編寫和運行 <b class='flag-5'>C</b> <b class='flag-5'>語言</b>程序?

    為什么學了C語言,卻寫不出像樣的項目?

    在學習編程的路上,C語言幾乎是每個程序員的“必修課”。不管你是打算從事嵌入式開發、系統編程,還是想要深入理解操作系統的底層原理,C
    的頭像 發表于 03-14 17:37 ?916次閱讀
    為什么學了<b class='flag-5'>C</b><b class='flag-5'>語言</b>,卻寫不出像樣的項目?

    單片機C語言編程寶典大全,初學必備【強推下載!】

    資料介紹:包含 1. 51單片機入門教程 2. 初學者適用:單片機C語言實例100例(代碼及釋義) 3. 51單片機C語言快速上手 4. 51單片機
    發表于 03-14 11:03