首頁 >後端開發 >C++ >C的對像模型如何工作,包括虛擬函數和繼承?

C的對像模型如何工作,包括虛擬函數和繼承?

Karen Carpenter
Karen Carpenter原創
2025-03-12 16:41:17959瀏覽

C的對像模型如何工作,包括虛擬函數和繼承

C的對像模型基於編譯時和運行時機制的組合,以支持繼承,多態性和封裝等特徵。從本質上講,它依靠班級的概念作為創建對象的藍圖。每個對像都是類的實例,其中同時包含數據(成員變量)和代碼(成員函數)。

繼承:繼承允許基於現有的(基礎類)創建新類(派生類)。派生的類繼承其基類的成員(數據和功能),可以添加自己的成員或覆蓋現有成員。這促進了代碼重用並建立“ IS-A”關係。例如, Dog課可能會從Animal階級繼承。

虛擬函數:虛擬函數是基類中使用virtual關鍵字聲明的成員函數。它們啟用運行時多態性,這意味著要調用的正確函數是根據對象的實際類型在運行時確定的,而不是其聲明的類型。這對於實現靈活性和可擴展性至關重要。它背後的機制是虛擬函數表(VTable) 。每個具有虛擬函數的類都有其自己的VTable,這是該類中實現的虛擬函數的指針表。包含虛擬函數的類的每個對像都有一個隱藏的指針(通常稱為VPTR),指向其類的VTable。當調用虛擬函數時,運行時使用VPTR在VTable中找到正確的函數。

例子:

 <code class="c  ">class Animal { public: virtual void makeSound() { std::cout makeSound(); // Calls Dog::makeSound() due to virtual function delete animal; return 0; }</code>

在此示例中, makeSound是虛擬函數。即使將animal宣佈為Animal指針,但由於可VT的機制,在運行時(從Dog類類中)的正確makeSound功能也被調用。

在C中使用虛擬函數的性能含義是什麼?

與非虛擬函數相比,使用虛擬函數引入了性能開銷。這個高架源於幾個因素:

  • 間接功能調用:訪問虛擬功能涉及額外的間接水平。該程序不要直接跳到函數的地址,而必須首先查閱VTable以找到正確的功能指針,然後跳到該地址。這增加了幾個CPU週期。
  • vtable尺寸和內存開銷:每個具有虛擬功能的類都需要VTable,這增加了程序的內存足跡。 VTable本身佔據內存,並且具有虛擬功能的類的每個對像都需要VPTR,從而增加了對象的大小。
  • 增加代碼大小:由於需要VTable和運行時調度機制,虛擬功能的實現可能導致代碼大小稍大。

但是,這些開銷通常很小,通常可以忽略不計,尤其是與虛擬功能提供的多態性和代碼可維護性的好處相比。現代編譯器採用各種優化技術來最大程度地降低虛擬功能的性能影響,例如內部和功能指針緩存。只有在代碼的性能 - 關鍵性段中調用虛擬函數時,性能影響才有意義,即使這樣,除非函數被稱為大量次數,否則差異通常是邊緣的。

C繼承如何影響內存管理和對像大小?

C繼承以多種方式影響內存管理和對像大小:

  • 對像大小:派生類通常比基類占據的內存更多,因為它們包含基類的所有成員變量以及自己的成員變量。派生類對象的大小至少是其基類尺寸和自己的成員的大小的總和,但是由於記憶對齊的填充,它可能更大。
  • 內存佈局:對象的確切內存佈局取決於編譯器和所使用的繼承模型(單個,多個,虛擬)。在單一繼承中,基類成員通常是第一位的,其次是派生的類成員。由於潛在的成員重複以及對虛擬基類指針的需求,多重和虛擬繼承引起了複雜性。
  • 內存管理:使用繼承時,內存管理變得更加複雜。派生階級的攻擊子是按照其基類的攻擊者的名字來調用的。這樣可以確保基本類分配的資源在派生類的資源之前發布。無法在繼承的類中正確管理內存會導致內存洩漏或懸空指針。在這種情況下,智能指針(例如, unique_ptrshared_ptr )可以簡化內存管理。
  • 虛擬繼承:虛擬繼承有助於避免多個繼承的問題,從而導致冗餘基類亞對象。它可以確保在派生類層次結構中只有一個虛擬基類的副本,即使多個繼承路徑導致了相同的虛擬基類。由於引入虛擬基類指針,這導致對象佈局的對像大小和復雜性增加。

您能在C虛擬函數的上下文中解釋靜態和動態調度之間的區別嗎?

靜態調度和動態調度是確定在運行時要調用哪種功能的兩種不同的方法。關鍵區別在於做出決定

  • 靜態調度(早期結合):靜態調度發生在編譯時。編譯器根據對象的靜態類型(代碼中聲明的類型)確定要調用的函數。非虛擬功能始終使用靜態調度。這是更快的,因為該函數調用是在編譯時直接解決的。
  • 動態調度(晚綁定):動態調度在運行時發生。編譯器使用對象的運行時類型(運行時對象的實際類型)來確定要調用哪個函數。這是通過虛擬函數的VTable機制來實現的。虛擬函數始終使用動態調度。這允許多態性,因為正確的函數被稱為對象的聲明類型。

說明區別的示例:

 <code class="c  ">class Animal { public: void makeSound() { std::cout makeSound(); // Static dispatch: Calls Animal::makeSound() animal->move(); // Dynamic dispatch: Calls Dog::move() delete animal; return 0; }</code>

在此示例中, makeSound使用靜態調度,因為它不是虛擬的,而move使用動態調度,因為它是虛擬的。這表明了virtual關鍵字的存在(或不存在)如何決定調度機制。

以上是C的對像模型如何工作,包括虛擬函數和繼承?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn