程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 那些不能遺忘的知識點回顧——C/C++系列,遺忘知識點

那些不能遺忘的知識點回顧——C/C++系列,遺忘知識點

編輯:C++入門知識

那些不能遺忘的知識點回顧——C/C++系列,遺忘知識點


有那麼一些零碎的小知識點,偶爾很迷惑,偶爾被忽略,偶然卻發現它們很重要,這段時間正好在溫習這些,就整理在這裡,一起學習一起提高!後面還會繼續補充。

——前言

1.面向對象的特性

  封裝、繼承、多態。

  封裝:把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。(優點:可以隱藏實現細節,使得代碼模塊化)

  繼承:可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展。(優點:可以擴展已存在的代碼模塊(類))

  多態:一個類實例的相同方法在不同情形有不同表現形式。多態機制使具有不同內部結構的對象可以共享相同的外部接口。雖然針對不同對象的具體操作不同,但通過一個公共的類,這些操作可以通過相同的方式被調用。

  多態實現的兩種方式:將子類對象的指針賦給父類類型的指針或將一個基類的引用指向它的派生類實例。(重要:虛函數 + 指針或引用)

構造函數、復制構造函數、析構函數、賦值運算符不能被繼承。

 

2.堆和棧

  從內存角度來說:棧區(stack)由編譯器自動分配釋放,存放函數的參數值,局部變變量的值等,其操作方式類似於數據結構中的棧,可靜態亦可動態分配。

  堆區(heap)一般由程序員分配釋放,若程序員不釋放,可能造成內存洩漏,程序結束時可能由OS回收。動態分配,分配方式類似於鏈表。

  從數據結構角度來說:堆可以被看成是一棵樹,如:堆排序。

  而棧是一種先進後出的數據結構。

 

3.malloc和new

  1.malloc與free是C++/C語言的標准庫函數,new/delete是C++的運算符。但它們都可用於申請動態內存和釋放內存。

  2.對於非內部數據類型的對象而言,用maloc/free無法滿足動態對象的要求。對象在創建的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數。由malloc/free是庫函數而不是運算符,不在編譯器控制權限之內,不能夠把執行構造函數和析構函數的任務強加於malloc/free,因此C++語言需要一個能完成動態內存分配和初始化工作的運算符new,和一個能完成清理與釋放內存工作的運算符delete。

  3.new可以認為是malloc加構造函數的執行。new出來的指針是直接帶類型信息的。而malloc返回的都是void*指針。new delete在實現上其實調用了malloc,free函數。

  4.new 建立的是一個對象;alloc分配的是一塊內存。

 

4.虛函數實現機制,虛繼承在sizeof中有沒有影響,構造函數能否為虛函數,與純虛函數

  虛函數表:類的虛函數表是一塊連續的內存,每個內存單元中記錄一個JMP指令的地址。

  編譯器會為每個有虛函數的類創建一個虛函數表,該虛函數表將被該類的所有對象共享。類的每個虛函數占據虛函數表中的一塊。如果類中有N個虛函數,那麼其虛函數表將有N*4字節的大小。

  在有虛函數的類的實例中分配了指向這個表的指針的內存,所以,當用父類的指針來操作一個子類的時候,這張虛函數表就顯得尤為重要了,它就像一個地圖一樣,指明了實際所應該調用的函數。

  編譯器應該是保證虛函數表的指針存在於對象實例中最前面的位置(這是為了保證取到虛函數表的有最高的性能——如果有多層繼承或是多重繼承的情況下)。 這意味著可以通過對象實例的地址得到這張虛函數表,然後就可以遍歷其中函數指針,並調用相應的函數。

  ->有虛函數或虛繼承的類實例化後的對象大小至少為4字節(還要加上其他非靜態數據成員,還要考慮對齊問題);沒有虛函數和虛繼承的類實例化後的對象大小至少為1字節(沒有非靜態數據成員的情況下也要有1個字節來記錄它的地址)。

  特別的:構造函數不能為虛函數。

 

5.面向對象的多態、多態的實現機制,多態的例子

 見知識點4

 

6.對一個類求sizeof需要考慮的內容

 見知識點4

 

7.重載和重寫(覆蓋)

  方法的重寫Overriding和重載Overloading是多態性的不同表現。

  重寫Overriding是父類與子類之間多態性的一種表現,重載Overloading是一個類中多態性的一種表現。

  如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫 (Overriding)。子類的對象使用這個方法時,將調用子類中的定義,對它而言,父類中的定義如同被“屏蔽”了,而且如果子類的方法名和參數類型和個數都和父類相同,那麼子類的返回值類型必須和父類的相同;如果在一個類中定義了多個同名的方法,它們或有不同的參數個數或有不同的參數類型,則稱為方法的重載(Overloading)。Overloading的方法是可以改變返回值的類型。也就是說,重載的返回值類型可以相同也可以不同。

 

8.“引用”與多態的關系?

  引用是除指針外另一個實現多態的方式。這意味著,一個基類的引用可以指向它的派生類實例。例:

  Class A; Class B : Class A{…};

  B b; A& ref = b;

 

9.計算機加載程序包括哪幾個區?

  一個由C/C++編譯的程序占用的內存分為以下幾個部分:

  (1)棧區(stack):—由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。可靜態也可動態分配。其操作方式類似於數據結構中的棧。 

  (2)堆區(heap):一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收。動態分配。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表。 

  (3)全局區(靜態區):—程序結束後由系統釋放,全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域;未初始化的全局變量和靜態變量在相鄰的另一塊區域(BSS,Block Started by Symbol),在程序執行之前BSS段會自動清0。 

  (4)文字常量區:—程序結束後由系統釋放,常量字符串就是放在這裡的。  

  (5)程序代碼區:—存放函數體的二進制代碼。

 

 

10.派生類中構造函數與析構函數,調用順序

  構造函數的調用順序總是如下:

  1.基類構造函數。如果有多個基類,則構造函數的調用順序是某類在類派生表中出現的順序,而不是它們在成員初始化表中的順序。

  2.成員類對象構造函數。如果有多個成員類對象則構造函數的調用順序是對象在類中被聲明的順序,而不是它們出現在成員初始化表中的順序。

  3.派生類構造函數。

  析構函數正好和構造函數相反。

 

11.extern的作用

  extern "C"實現C++與C及其它語言的混合編程,是用在C和C++之間的橋梁。之所以需要這個橋梁是因為C編譯器編譯函數時不帶函數的類型信息,只包含函數符號名字;而C++編譯器為了實現函數重載,編譯時會帶上函數的類型信息,如他把上面的a函數可能編譯成_a_float這樣的符號為了實現重載。

  extern "C"的慣用法:

  在C++中引用C語言中的函數和變量,在包含C語言頭文件(假設為cExample.h)時,需進行下列處理:

  extern "C"{

    #include "cExample.h"

  }

  而在C語言的頭文件中,對其外部函數只能指定為extern類型,C語言中不支持extern "C"聲明,在.c文件中包含了extern "C"時會出現編譯語法錯誤。

 

12.析構函數、構造函數能不能被繼承

  見知識點1

 

13.C++為什麼用模板類,為什麼用泛型

  通過泛型可以定義類型安全的數據結構(類型安全),而無須使用實際的數據類型(可擴展)。這能夠顯著提高性能並得到更高質量的代碼(高性能),因為您可以重用數據處理算法,而無須復制類型特定的代碼(可重用)。

 

14.結構體內存對齊,與什麼有關(CPU)

  在系統默認的對齊方式下:每個成員相對於這個結構體變量地址的偏移量正好是該成員類型所占字節的整數倍,且最終占用字節數為成員類型中最大占用字節數的整數倍。

  詳細分析見博客http://www.cnblogs.com/webary/p/4721017.html

  為什麼要對齊?當CPU訪問正確對齊的數據時,它的運行效率最高,當數據大小的數據模數的內存地址是0時,數據是對齊的。例如:WORD值應該是總是從被2除盡的地址開始,而DWORD值應該總是從被4除盡的地址開始,數據對齊不是內存結構的一部分,而是CPU結構的一部分。當CPU試圖讀取的數值沒有正確的對齊時,CPU可以執行兩種操作之一:產生一個異常條件;執行多次對齊的內存訪問,以便讀取完整的未對齊數據,若多次執行內存訪問,應用程序的運行速度就會慢。

 

15.指針和引用

  1.指針是一個變量,只不過這個變量存儲的是一個地址,指向內存的一個存儲單元;而引用跟原來的變量實質上是同一個東西,只不過是原變量的一個別名而已。

  2.指針可以有多級,但是引用只能是一級;

  3.指針的值可以為空,也可能指向一個不確定的內存空間,但是引用的值不能為空,並且引用在定義的時候必須初始化為特定對象;(因此引用更安全)

  4.指針的值在初始化後可以改變,即指向其它的存儲單元,而引用在進行初始化後就不會再改變引用對象了;

  5.sizeof引用得到的是所指向的變量(對象)的大小,而sizeof指針得到的是指針本身的大小;

  6.指針和引用的自增(++)運算意義不一樣;

 

16.static關鍵字作用

  在C語言中,關鍵字static有三個明顯的作用:

  1)在函數體內,一個被聲明為靜態的變量在這一函數被調用過程中維持上一次的值不變,即只初始化一次(該變量存放在靜態變量區)。

  2)在模塊內(但在函數體外),一個被聲明為靜態的變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問。它是一個本地的全局變量。(注:模塊可以理解為文件)

  3)在模塊內,一個被聲明為靜態的函數只可被這一模塊內的其它函數調用。那就是,這個函數被限制在聲明它的模塊的本地范圍內使用。

  除此之外,C++中還有新用法:

  4)在類中的static成員變量意味著它為該類的所有實例所共享,也就是說當某個類的實例修改了該靜態成員變量,其修改值為該類的其它所有實例所見;

  5)在類中的static成員函數屬於整個類所擁有,這個函數不接收this指針,因而只能訪問類的static成員變量(當然,可以通過傳遞一個對象來訪問其成員)。

 

17.虛表,基類的虛表是什麼樣的,派生類虛表

  (1)單繼承情況

  (2)多重繼承(無虛函數覆蓋)

  (3)多重繼承(有虛函數覆蓋)

   詳細的內容參考博文:關於C++虛函數表的那些事兒

 

18.volatile

  volatile關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素更改,比如:操作系統、硬件或者其它線程等。遇到這個關鍵字聲明的變量,編譯器對訪問該變量的代碼就不再進行優化,從而可以提供對特殊地址的穩定訪問。

  當要求使用volatile 聲明的變量的值的時候,系統總是重新從它所在的內存讀取數據,即使它前面的指令剛剛從該處讀取過數據。而且讀取的數據立刻被保存。

  volatile 指出 i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,因而編譯器生成的匯編代碼會重新從i的地址讀取數據放在b中。而優化做法是,由於編譯器發現兩次從i讀數據的代碼之間的代碼沒有對i進行過操作,它會自動把上次讀的數據放在b中。而不是重新從i裡面讀。這樣一來,如果i是一個寄存器變量或者表示一個端口數據就容易出錯,所以說volatile可以保證對特殊地址的穩定訪問。

 

C++系列的暫時整理到這裡吧,如果讀者發現還有哪些這方面的經典常考知識點也請指出,待續~

轉載請注明出處,謝謝!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved