18luck网站

18luck网站電子設計 | 18luck网站Rss 2.0 會員中心 會員注冊
搜索: 您現在的位置: 18luck网站 >> 編程學習 >> C語言 >> 正文

C語言和C++的區別和聯係_麵向過程語言和麵向對象語言

作者:佚名    文章來源:本站原創    點擊數:    更新時間:2023/10/10

C語言雖說經常和C++在一起被大家提起,但可千萬(wan) 不要以為(wei) 它們(men) 是一種編程語言。我們(men) 來介紹C語言和C++中的區別和聯係。

首先C++和C語言本來就是兩(liang) 種不同的編程語言,但C++確實是對C語言的擴充和延伸,並且對C語言提供後向兼容的能力。

更多C語言/C++學習(xi) 資料,視頻,電子書(shu) 籍,教程
 

一、C語言是麵向過程語言,而C++是麵向對象語言

我們(men) 都知道C語言是麵向過程語言,而C++是麵向對象語言,說C語言和C++的區別和聯係,也就是在比較麵向過程和麵向對象的區別。

1、麵向過程和麵向對象的區別

麵向過程:麵向過程編程就是分析出解決(jue) 問題的步驟,然後把這些步驟一步一步的實現,使用的時候一個(ge) 一個(ge) 的依次調用就可以了。

麵向對象:麵向對象編程就是把問題分解成各個(ge) 對象,建立對象的目的不是為(wei) 了完成一個(ge) 步驟,而是為(wei) 了描述某個(ge) 事物在整個(ge) 解決(jue) 問題的步驟中的行為(wei) 。

2、麵向過程和麵向對象的優(you) 缺點

在學習(xi) 一些比較抽象的概念時,由於(yu) 我們(men) 的理解能力很有限,有時候一些比較恰當的例子也是有助於(yu) 我們(men) 學習(xi) 的,因此對二者的優(you) 缺點比較,還是先舉(ju) 例子,後總結吧!

(1)用麵向過程寫(xie) 出來的程序就像一份蛋炒飯,也就是米飯和炒的菜均勻的混合在了一起,因此蛋炒飯入味均勻,不會(hui) 像蓋澆飯那樣,可能有時候吃的菜多飯少,還有時候菜少飯多。但是如果你不喜歡吃蛋炒飯,隻想吃肉炒飯,那麽(me) 原來做的這份蛋炒飯就得倒掉了,重新做一份肉炒飯,廚師就得辛苦了!

(2)用麵向對象寫(xie) 出來的程序就像一份蓋澆飯,也就是米飯和蓋菜分別做好,將蓋菜放在米飯上麵,蓋澆飯雖然沒有蛋炒飯那樣入味均勻,但是如果給了你一份土豆絲(si) 蓋飯,你又不想吃了,換成牛肉蓋飯,廚師隻需要將米飯上麵的土豆絲(si) 倒掉,重新做一份牛肉放在上麵就好了。

那麽(me) 到底蛋炒飯和蓋澆飯哪個(ge) 好吃呢?

誰也不能說到底哪個(ge) 好,畢竟蛋炒飯的餐館和蓋澆飯的餐館都很多,而且生意都很不錯,存在即為(wei) 合理!

如果非要將二者進行一個(ge) 高地的比較的話,那就得先設定一個(ge) 場景了!

蓋澆飯的好處就是”菜”“飯”分離,從(cong) 而提高了製作蓋澆飯的靈活性。飯不滿意就換飯,菜不滿意換菜。用專(zhuan) 業(ye) 術語來說就是”可維護性“較好,”飯” 和”菜”的耦合度比較低。

蛋炒飯將”蛋”“飯”攪和在一起,想換”蛋”“飯”中任何一種都很困難,耦合度很高,以至於(yu) ”可維護性”比較差。

二者的簡單總結如下:

麵向過程語言

優(you) 點:性能比麵向對象高,因為(wei) 類調用時需要實例化,開銷比較大,比較消耗資源;比如單片機、嵌入式開發、 Linux/Unix等一般采用麵向過程開發,性能是最重要的因素。

缺點:沒有麵向對象易維護、易複用、易擴展

麵向對象語言:

優(you) 點:易維護、易複用、易擴展,由於(yu) 麵向對象有封裝、繼承、多態性的特性,可以設計出低耦合的係統,使係統 更加靈活、更加易於(yu) 維護

缺點:性能比麵向過程低

二、具體語言上C語言和C++的區別和聯係

1、關(guan) 鍵字的不同

C語言有32個(ge) 關(guan) 鍵字

C++有63個(ge) 關(guan) 鍵字

2、後綴名不同

C源文件後綴.c,C++源文件後綴.cpp,在VS中,如果在創建源文件時什麽(me) 都不給,默認是.cpp。

3、返回值

C語言中,如果一個(ge) 函數沒有指定返回值類型,默認返回int類型;C++中,如果一個(ge) 函數沒有返回值則必須指定為(wei) void。

4、參數列表

在C語言中,函數沒有指定參數列表時,默認可以接收任意多個(ge) 參數;但在C++中,因為(wei) 嚴(yan) 格的參數類型檢測,沒有參數列表的函數,默認為(wei) void,不接收任何參數。

5、缺省參數

缺省參數是聲明或定義(yi) 函數時為(wei) 函數的參數指定一個(ge) 默認值。在調用該函數時,如果沒有指定實參則采用該默認值,否則使用指定的參。(C語言不支持缺省參數)

注意:

  • 在半缺省的情況下,帶缺省值的參數必須放在參數列表的最後麵。

  • 缺省參數不能同時在函數的聲明和函數定義(yi) 中出現,二者隻能選其一。

  • 缺省值必須是常量或者全局變量。

  • 缺省參數必須通過值參或常參傳(chuan) 遞。

6、函數重載

函數重載:函數重載是函數的一種特殊情況,指在同一作用域中,聲明幾個(ge) 功能類似的同名函數,這些同名函數的形參列表(參數個(ge) 數、類型、順序)必須不同,返回值類型可以相同也可以不同,常用來處理實現功能類似數據類型不同的問題。(C語言沒有函數重載,C++支持函數重載)。

C語言中產(chan) 生函數符號的規則是根據名稱產(chan) 生,這也就注定了c語言不存在函數重載的概念。而C++生成函數符號則考慮了函數名、參數個(ge) 數、參數類型。需要注意的是函數的返回值並不能作為(wei) 函數重載的依據,也就是說int sum和double sum這兩(liang) 個(ge) 函數是不能構成重載的!

我們(men) 的函數重載也屬於(yu) 多態的一種,這就是所謂的靜多態。

靜多態:函數重載,函數模板

動多態(運行時的多態):繼承中的多態(虛函數)。

使用重載的時候需要注意作作用域問題:請看如下代碼。

#include using namespace std; bool compare(int a,int b) { return a > b; } bool compare(double a,double b) { return a > b; } int main() { //bool compare(int a,int b); compare(10,20); compare(10.5,20.5); return 0; }

我在全局作用域定義(yi) 了兩(liang) 個(ge) 函數,它們(men) 由於(yu) 參數類型不同可以構成重載,此時main函數中調用則可以正確的調用到各自的函數。

但是請看main函數中被注釋掉的一句代碼。如果我將它放出來,則會(hui) 提出警告:將double類型轉換成int類型可能會(hui) 丟(diu) 失數據。

這就意味著我們(men) 編譯器針對下麵兩(liang) 句調用都調用了參數類型int的compare。由此可見,編譯器調用函數時優(you) 先在局部作用域搜索,若搜索成功則全部按照該函數的標準調用。若未搜索到才在全局作用域進行搜索。

總結:C語言不存在函數重載,C++根據函數名參數個(ge) 數參數類型判斷重載,屬於(yu) 靜多態,必須同一作用域下才叫重載。

7、const

C語言中被const修飾的變量不是常量,叫做常變量或者隻讀變量,這個(ge) 常變量是無法當作數組下標的。然而在C++中const修飾的變量可以當作數組下標使用,成為(wei) 了真正的常量。這就是C++對const的擴展。

C語言中的const:被修飾後不能做左值,可以不初始化,但是之後沒有機會(hui) 再初始化。不可以當數組的下標,可以通過指針修改。簡單來說,它和普通變量的區別隻是不能做左值而已。其他地方都是一樣的。

C++中的const:真正的常量。定義(yi) 的時候必須初始化,可以用作數組的下標。const在C++中的編譯規則是替換(和宏很像),所以它被看作是真正的常量。也可以通過指針修改。需要注意的是,C++的指針有可能退化成C語言的指針。比如以下情況:

int b=20; const int a = b;

這時候的a就隻是一個(ge) 普通的C語言的const常變量了,已經無法當數組的下標了。(引用了一個(ge) 編譯階段不確定的值)

const在生成符號時,是local符號。即在本文件中才可見。如果非要在別的文件中使用它的話,在文件頭部聲明:extern cosnt int data = 10;這樣生成的符號就是global符號。

總結:C中的const叫隻讀變量,隻是無法做左值的變量;C++中的const是真正的常量,但也有可能退化成c語言的常量,默認生成local符號。

8、引用

說到引用,我們(men) 第一反應就是想到了他的兄弟:指針。引用從(cong) 底層來說和指針就是同一個(ge) 東(dong) 西,但是在編譯器中它的特性和指針完全不同。

int a = 10; int &b=a; int *p = &a; // b = 20; // *p=20;

首先定義(yi) 一個(ge) 變量a = 10,然後我們(men) 分別定義(yi) 一個(ge) 引用b以及一個(ge) 指針p指向a。我們(men) 來轉到反匯編看看底層的實現:

 

可以看到底層實現完全一致,取a的地址放入eax寄存器,再將eax中的值存入引用b/指針p的內(nei) 存中。至此我們(men) 可以說(在底層)引用本質就是一個(ge) 指針。

了解了底層實現,我們(men) 回到編譯器。我們(men) 看到對a的值的修改,指針p的做法是*p = 20;即進行解引用後替換值。底層實現:

再來看看引用修改:

我們(men) 看到修改a的值的方法也是一樣的,也是解引用。隻是我們(men) 在調用的時候有所不同:調用p時需要*p解引用,b則直接使用就可以。由此我們(men) 推斷出:引用在直接使用時是指針解引用。p直接使用則是它自己的地址。

這樣我們(men) 也了解了,我們(men) 給引用開辟的這塊內(nei) 存是根本訪問不到的。如果直接用就直接解引用了。即使打印&b,輸出的也是a的地址。

在此附上將指針轉為(wei) 引用的小技巧:int *p = &a,我們(men) 將 引用符號移到左邊 將 *替換即可:int &p = a。

接下來看看如何創建數組的引用:

int array[10] = {0}; //定義(yi) 一個(ge) 數組

我們(men) 知道,array拿出來使用的話就是數組array的首元素地址。即是int *類型。

那麽(me) &array是什麽(me) 意思呢?int **類型,用來指向array[0]地址的一個(ge) 地址嗎?不要想當然了,&array是整個(ge) 數組類型。

那麽(me) 要定義(yi) 一個(ge) 數組引用,按照上麵的小訣竅,先來寫(xie) 寫(xie) 數組指針吧:

int (*q) [10] = &array;

將右側(ce) 的&對左邊的*進行覆蓋:

int (&q)[10] = array;

測試sizeof(q) = 10。我們(men) 成功創建了數組引用。

經過上麵的詳解 ,我們(men) 知道了引用其實就是取地址。那麽(me) 我們(men) 都知道一個(ge) 立即數是沒有地址的,即

int &b = 10;

這樣的代碼是無法通過編譯的。那如果你就是非要引用一個(ge) 立即數,其實也不是沒有辦法:

const int &b = 10;

即將這個(ge) 立即數用const修飾一下,就可以了。為(wei) 什麽(me) 呢?

這時因為(wei) 被const修飾的都會(hui) 產(chan) 生一個(ge) 臨(lin) 時量來保存這個(ge) 數據,自然就有地址可取了。

9、malloc,free && new,delete

這個(ge) 問題很有意思,也是重點需要關(guan) 注的問題。malloc()和free()是C語言中動態申請內(nei) 存和釋放內(nei) 存的標準庫中的函數。而new和delete是C++運算符、關(guan) 鍵字。new和delete底層其實還是調用了malloc和free。它們(men) 之間的區別有以下幾個(ge) 方麵:

1)、malloc和free是函數,new和delete是運算符。

2)、malloc在分配內(nei) 存前需要大小,new不需要。

例如:int *p1 = (int *)malloc(sizeof(int));

int *p2 = new int; //int *p3 = new int(10);

malloc時需要指定大小,還需要類型轉換。new時不需要指定大小因為(wei) 它可以從(cong) 給出的類型判斷,並且還可以同時賦初始值。

3)、malloc不安全,需要手動類型轉換,new不需要類型轉換。

詳見上一條。

4)、free隻釋放空間,delete先調用析構函數再釋放空間(如果需要)。

與(yu) 第⑤條對應,如果使用了複雜類型,先析構再call operator delete回收內(nei) 存。

5)、new是先調用構造函數再申請空間(如果需要)。

與(yu) 第④條對應,我們(men) 在調用new的時候(例如int *p2 = new int;這句代碼 ),底層代碼的實現是:首先push 4字節(int類型的大小),隨後call operator new函數分配了內(nei) 存。由於(yu) 我們(men) 這句代碼並未涉及到複雜類型(如類類型),所以也就沒有構造函數的調用。如下是operator new的源代碼,也是new實現的重要函數:

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) { // try to allocate size bytes void *p; while ((p = malloc(size))==0) if ( _callnewh(size)==0) { // report no memory THROW_NCEE(_XSTD bad alloc,); } return (p); }

 

我們(men) 可以看到,首先malloc(size)申請參數字節大小的內(nei) 存,如果失敗(malloc失敗返回0)則進入判斷:如果_callnewh(size)也失敗的話,拋出bad_alloc異常。_callnewh()這個(ge) 函數是在查看new handler是否可用,如果可用會(hui) 釋放一部分內(nei) 存再返回到malloc處繼續申請,如果new handler不可用就會(hui) 拋出異常。

6)、內(nei) 存不足(開辟失敗)時處理方式不同。

malloc失敗返回0,new失敗拋出bad_alloc異常。

7)、new和malloc開辟內(nei) 存的位置不同。

malloc開辟在堆區,new開辟在自由存儲(chu) 區域。

8)、new可以調用malloc(),但malloc不能調用new。

new就是用malloc()實現的,new是C++獨有malloc當然無法調用。

10、作用域

C語言中作用域隻有兩(liang) 個(ge) :局部,全局。C++中則是有:局部作用域,類作用域,名字空間作用域三種。

所謂名字空間就是namespace,我們(men) 定義(yi) 一個(ge) 名字空間就是定義(yi) 一個(ge) 新作用域。訪問時需要以如下方式訪問(以std為(wei) 例)

std::cin<< "123" <<std::endl;

例如我們(men) 有一個(ge) 名字空間叫Myname,其中有一個(ge) 變量叫做data。如果我們(men) 希望在其他地方使用data的話,需要在文件頭聲明:using Myname::data;這樣一來data就使用的是Myname中的值了。可是這樣每個(ge) 符號我們(men) 都得聲明豈不是累死?

我們(men) 隻要using namespace Myname;就可以將其中所有符號導入了。

Tags:C語言,麵向過程語言,麵向對象語言  
責任編輯:admin
請文明參與討論,禁止漫罵攻擊。 昵稱:注冊  登錄
[ 查看全部 ] 網友評論
關於我們 - 聯係我們 - 廣告服務 - 友情鏈接 - 網站地圖 - 版權聲明 - 在線幫助 - 文章列表
返回頂部
刷新頁麵
下到頁底
晶體管查詢