知識點總結(六)

2020-08-10 10:13:58

1、C與C++的區別?
設計思想上:
C++是物件導向的語言,而C是程序導向的結構化程式語言

語法上:
C++具有封裝、繼承和多型三種特性
C++支援函數過載和預設參數
關鍵字個數也不同

2、什麼是程序導向?什麼是物件導向?
程序導向就是分析出解決問題所需要的步驟,然後用函數把這些步驟一步一步實現,使用的時候一個一個依次呼叫就可以了;(自頂向下,逐步細化

物件導向是把構成問題事務分解成各個物件,建立物件的目的不是爲了完成一個步驟,而是爲了描敘某個事物在整個解決問題的步驟中的行爲,物件導向是以功能來劃分問題,而不是步驟。
例:
五子棋
程序導向的設計思路就是首先分析問題的步驟:
1、開始遊戲,
2、黑子先走,
3、繪製畫面,
4、判斷輸贏,
5、輪到白子,
6、繪製畫面,
7、判斷輸贏,
8、返回步驟2,
9、輸出最後結果。把上面每個步驟用不同的方法來實現。

物件導向的設計則是從另外的思路來解決問題。整個五子棋可以分爲
1、黑白雙方,這兩方的行爲是一模一樣的,
2、棋盤系統,負責繪製畫面,
3、規則系統,負責判定諸如犯規、輸贏等。第一類物件(玩家物件)負責接受使用者輸入,並告知第二類物件(棋盤物件)棋子佈局的變化,棋盤物件接收到了棋子的變化就要負責在螢幕上面顯示出這種變化,同時利用第三類物件(規則系統)來對棋局進行判定。

3、封裝、繼承、多型的概念。
封裝可以隱藏實現細節,使得程式碼模組化:封裝是把過程和數據包圍起來,對數據的存取只能通過已定義的介面。對於封裝的物件,這些物件通過一個受保護的介面存取其他物件。在物件導向程式設計上可理解爲:將數據和操作數據的方法進行有機結合,隱藏物件的屬性和實現細節,僅對外公開介面來和物件進行 互動。

繼承可以擴充套件已存在的程式碼模組(類)——程式碼複用: 它可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴充套件。其繼承的過程,就是從一般到特殊的過程。

多型則是爲了實現另一個目的——介面複用: 多型簡單說,就是某一類事物多種存在形態。多型性可以簡單概括爲「一個介面,多種實現」,是通過虛擬函式實現的。基礎類別提供一個虛介面,其派生類重寫這個介面,這樣就構成了多型。

4、什麼是多型性?
C++的多型性就是:在基礎類別的函數前加上virtual關鍵字,在派生類中重寫該函數,執行時將會根據物件的實際型別來呼叫相應的函數。如果物件型別是派生類,就呼叫派生類的函數;如果物件型別是基礎類別,就呼叫基礎類別的函數。

5、多型是怎麼實現的?
具體實現:

  1. 多型是在繼承的基礎上通過虛擬函式重寫實現的。
  2. 每個帶有虛擬函式的類中都有一個虛表指針,該指針指向類的虛擬函式表。
  3. 虛表可以被繼承,如果子類沒有重寫虛擬函式,那麼子類虛表中仍然會有該函數的地址,只不過這個地址指向的是基礎類別的虛擬函式實現。如果派生類重寫了基礎類別中相對應的虛擬函式,那麼派生類虛表中的該函數的地址就會被覆蓋,變爲指向自身的虛擬函式實現。
  4. 派生類的虛表中虛擬函式地址的排列順序和基礎類別的虛表中虛擬函式地址排列順序相同。

6、C++中struct和class的區別是什麼?
C++需要相容C語言,所以C++中struct可以當成結構體去使用。另外C++中struct還可以用來定義類。
和class是定義類是一樣的,區別是struct的成員預設存取方式是public,class的成員預設存取方式是private。

7、類中的成員怎麼儲存的?怎麼計算大小?
類中只儲存成員變數,成員函數存放在公共的程式碼段 。

一個類的大小,實際就是該類中」成員變數」之和,注意需要進行記憶體對齊。如果類中有虛擬函式,則還需要加上一個虛擬函式指針的大小。
注意空類的大小,空類比較特殊,編譯器給了空類一個位元組來唯一標識這個類。

8、 結構體怎麼對齊? 爲什麼要進行記憶體對齊 ?

  1. 1) 第一個成員在與結構體偏移量爲0的地址處。
    2)其他成員變數要對齊到某個數位(對齊數)的整數倍的地址處。
    注意:對齊數 = 編譯器預設的一個對齊數與該成員大小的較小值。

  2. 1)平臺原因(移植原因):不是所有的硬體平臺都能存取任意地址上的任意數據的;某些硬體平臺只能在某些地址處取某些特定型別的數據,否則拋出硬體異常。
    2)效能原因:數據結構(尤其是棧)應該儘可能地在自然邊界上對齊。原因在於,爲了存取未對齊的記憶體,處理器需要作兩次記憶體存取;而對齊的記憶體存取僅需要一次存取。

9、什麼是this指針?
C++編譯器給每個「非靜態的成員函數「增加了一個隱藏的指針參數,讓該指針指向當前物件(函數執行時呼叫該函數的物件),在函數體中所有成員變數的操作,都是通過該指針去存取。只不過所有的操作對使用者是透明的,即使用者不需要來傳遞,編譯器自動完成。

10、this指針存在哪裏?
其實編譯器在生成程式時加入了獲取物件首地址的相關程式碼。並把獲取的首地址存放在了暫存器ECX中(VC++編譯器是放在ECX中,其它編譯器有可能不同)。也就是成員函數的其它參數正常都是存放在棧中。而this指針參數則是存放在暫存器中。類的靜態成員函數因爲沒有this指針這個參數,所以類的靜態成員函數也就無法呼叫類的非靜態成員變數。

11、this指針可以爲空嗎?
可以爲空,當我們在呼叫函數的時候,如果函數內部並不需要使用到this,也就是不需要通過this指向當前物件並對其進行操作時纔可以爲空(當我們在其中什麼都不放或者在裏面隨便列印一個字串),如果呼叫的函數需要指向當前物件,並進行操作,則會發生錯誤(空指針參照)就跟C中一樣不能進行空指針的參照。

12、類的預設成員函數有哪些?

  1. 建構函式
  2. 解構函式
  3. 拷貝(複製)建構函式
  4. 賦值運算子過載
  5. 取地址運算子過載
  6. const修飾的取地址運算子過載

13、什麼是過載、重寫、重定義?
過載:在同一個作用域,函數名和參數相同,參數型別不同。
重寫:函數分別在基礎類別和派生類的作用域中,函數名、參數、返回值都要相同,且函數爲虛擬函式。(協變除外)
重定義:基礎類別和派生類中的同名函數不構成重寫就是重定義。

14、C++的記憶體分佈?

  1. 棧又叫堆疊,非靜態區域性變數/函數參數/返回值等等,棧是向下增長的。
  2. 記憶體對映段是高效的I/O對映方式,用於裝載一個共用的動態記憶體庫。使用者可使用系統介面建立共用記憶體,做進程間通訊。
  3. 堆用於程式執行時動態記憶體分配,堆是可以上增長的。
  4. 數據段–儲存全域性數據和靜態數據。
  5. 程式碼段–可執行的程式碼/只讀常數。

15、malloc/calloc/realloc的區別?

  1. malloc:在堆記憶體中分配一塊指定位元組的連續區域。
  2. calloc:與malloc相似,在堆記憶體中分配n個申請型別大小的連續空間。並對申請空間初始化爲0。
  3. realloc:對已經存在的空間進行大小調整。

16、malloc/free和new/delete的區別?
malloc/free和new/delete的共同點是:都是從堆上申請空間,並且需要使用者手動釋放。
不同的地方是:

  1. malloc和free是函數,new和delete是操作符 。
  2. malloc申請的空間不會初始化,new可以進行初始化 。
  3. malloc申請空間時,需要手動計算空間大小並傳遞,new只需在其後跟上空間的型別即可 。
  4. malloc的返回值爲void*, 在使用時必須強轉,new不需要,因爲new後跟的是空間的型別 。
  5. malloc申請空間失敗時,返回的是NULL,因此使用時必須判空,new不需要,但是new需要捕獲異常 。
  6. 申請自定義型別物件時,malloc/free只會開闢空間,不會呼叫建構函式與解構函式,而new在申請空間 後會呼叫建構函式完成物件的初始化,delete在釋放空間前會呼叫解構函式完成空間中資源的清理。

17、什麼是記憶體漏失?記憶體漏失的危害?
什麼是記憶體漏失:記憶體漏失指因爲疏忽或錯誤造成程式未能釋放已經不再使用的記憶體的情況。 記憶體漏失並不是指記憶體在物理上的消失,而是應用程式分配某段記憶體後,因爲設計錯誤,失去了對該段記憶體的控制,因而造成了記憶體的浪費。

記憶體漏失的危害:長期執行的程式出現記憶體漏失,影響很大,如操作系統、後臺服務等等,出現記憶體漏失會 導致響應越來越慢,最終卡死。

18、如何避免記憶體漏失?

  1. 工程前期良好的設計規範,養成良好的編碼規範,申請的記憶體空間記着匹配的去釋放。
  2. 採用RAII思想或者智慧指針來管理資源。
  3. 寫完程式碼後使用記憶體漏失工具檢測。