程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 深刻解析C++編程中基類與基類的繼續的相干常識

深刻解析C++編程中基類與基類的繼續的相干常識

編輯:關於C++

深刻解析C++編程中基類與基類的繼續的相干常識。本站提示廣大學習愛好者:(深刻解析C++編程中基類與基類的繼續的相干常識)文章只能為提供參考,不一定能成為您想要的結果。以下是深刻解析C++編程中基類與基類的繼續的相干常識正文


基類
繼續進程將創立一個新的派生類,它由基類的成員加上派生類添加的任何新成員構成。在多重繼續中,可以構建一個繼續關系圖,個中雷同的基類是多個派生類的一部門。下圖顯示了此類關系圖。

單個基類的多個實例
在該圖中,顯示了 CollectibleString 和 CollectibleSortable 的組件的圖形化表現情勢。然則,基類 Collectible 位於經由過程 CollectibleSortableString 途徑和 CollectibleString 途徑的 CollectibleSortable 中。若要清除此冗余,可以在繼續此類類時將其聲明為虛擬基類。

多個基類
如多重繼續中所述,類可以從多個基類派生。在多重繼續模子中(個中,類派生自多個基類),應用 base-list 語法元素指定基類(請參閱概述中的“語法”一節)。例如,可以指定派生自 CollectionOfBook 和 Collection 的 Book 的類聲明:

// deriv_MultipleBaseClasses.cpp
// compile with: /LD
class Collection {
};
class Book {};
class CollectionOfBook : public Book, public Collection {
 // New members
};

指定基類的次序其實不主要,只不外在某些情形下,將挪用結構函數和析構函數。在這些情形下,指定基類的次序將影響:
結構函數停止初始化的次序。假如您的代碼依附要在 Book 部門之前初始化的 CollectionOfBook 的 Collection 部門,則標准的次序很主要。依照 base-list 中指定類的次序履行初始化。
挪用析構函數以停止清算的次序。異樣,假如在燒毀另外一部門時必需出現類的特定“部門”,則次序異常主要。依照與 base-list 中指定類的次序相反的次序挪用析構函數。
留意
基類的標准次序會影響類的內存結構。不要基於內存中基成員的次序做出任何編程決議計劃。
當指定 base-list 時,不克不及屢次指定統一類名。然則,可以將類屢次作為派生類的直接基。

虛擬基類
因為一個類能夠屢次成為派生類的直接基類,是以 C++ 供給了一種優化這類基類的任務方法的辦法。虛擬基類供給了一種節儉空間和防止應用多重繼續的類條理構造中湧現多義性的辦法。
每一個非虛擬對象包括在基類中界說的數據成員的一個正本。這類反復糟蹋了空間,並請求您在每次拜訪基類成員時都必需指定所需的基類成員的正本。
當將某個基類指定為虛擬基時,該基類可以屢次作為直接基而無需復制其數據成員。基類的數據成員的單個正本由將其用作虛擬基的一切基類同享。
當聲明虛擬基類時,virtual 症結字將顯示在派生類的基列表中。
請斟酌下圖中的類條理構造,它演示了模仿的午飯列隊。

模仿午飯列隊圖
在該圖中,Queue 是 CashierQueue 和 LunchQueue 的基類。然則,當將這兩個類組分解 LunchCashierQueue 時,會湧現以下成績:新類包括類型 Queue 的兩個子對象,一個來自 CashierQueue,另外一個來自 LunchQueue。下圖顯示了概念上的內存結構(現實物理內存結構能夠會停止優化)。

模仿午飯列隊對象
請留意,Queue 對象中有兩個 LunchCashierQueue 子對象。以下代碼將 Queue 聲明為虛擬基類:

// deriv_VirtualBaseClasses.cpp
// compile with: /LD
class Queue {};
class CashierQueue : virtual public Queue {};
class LunchQueue : virtual public Queue {};
class LunchCashierQueue : public LunchQueue, public CashierQueue {};

virtual 症結字可確保只包括子對象 Queue 的一個正本(請參閱下圖)。

應用虛擬基類模仿午飯列隊對象
一個類可以同時具有一個給定類型的虛擬組件和非虛擬組件。下圖演示了這類情形。

統一個類的虛擬組件與非虛擬組件
在圖中,CashierQueue 和 LunchQueue 將 Queue 用作虛擬基類。然則,TakeoutQueue 將 Queue 指定為基類而不是虛擬基類。是以,LunchTakeoutCashierQueue 具有類型 Queue 的兩個子對象:一個來自包括 LunchCashierQueue 的繼續途徑,另外一個來自包括 TakeoutQueue 的途徑。下圖對此停止了演示。

帶虛擬和非虛擬繼續的對象結構
留意
與非虛擬繼續比擬較,虛擬繼續供給了明顯的年夜小優勢。然則,它能夠會引入額定的處置開支。
假如派生類重寫它從虛擬基類繼續的虛函數,而且派生基類的結構函數或析構函數應用指向虛擬基類的指針挪用該虛函數,則編譯器能夠會將其他隱蔽的“vtordisp”字段引入到具有虛擬基的類中。/vd0 編譯器選項將制止添加隱蔽的 vtordisp 結構函數/析構函數置換成員。默許的 /vd1 編譯器選項會在需要時啟用它們。僅當肯定一切類結構函數和析構函數以虛擬方法挪用虛函數時才封閉 vtordisps。
/vd 編譯器選項會影響全部編譯模塊。應用 vtordisp 雜注可以逐一類地禁用 vtordisp 字段,然後從新啟用這些字段:

#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )

關於後面的類聲明,以下所示的代碼是不明白的,由於 b 所指的 b 是在 A 中照樣在 B 中其實不清晰:

C *pc = new C;

pc->b();

稱號多義性
多重繼續使得沿多個途徑繼續稱號成為能夠。沿這些途徑的類成員稱號紛歧定是獨一的。這些稱號抵觸稱為“多義性”。
任何援用類成員的表達式必需采取明白的援用。以下示例解釋若何發生多義性:

// deriv_NameAmbiguities.cpp
// compile with: /LD
// Declare two base classes, A and B.
class A {
public:
 unsigned a;
 unsigned b();
};

class B {
public:
 unsigned a(); // Note that class A also has a member "a"
 int b();  // and a member "b".
 char c;
};

// Define class C as derived from A and B.
class C : public A, public B {};

請看後面的示例。因為稱號 a 是類 A 和類 B 的成員,是以編譯器沒法辯明哪一個 a 指定將挪用函數。假如成員可以援用多個函數、對象、類型或列舉數,則對該成員的拜訪是不明白的。
編譯器經由過程按此次序履行測試來檢測多義性:
假如對稱號的拜訪是不明白的(如上所述),則會生成毛病新聞。
假如重載函數是明白的,則將解析它們。(有關函數重載多義性的具體信息,請參閱參數婚配。)
假如對稱號的拜訪違反了成員拜訪權限,則會生成毛病新聞。(有關具體信息,請參閱成員拜訪掌握。)
在表達式經由過程繼續發生多義性時,您可以經由過程限制斟酌中的稱號及其類名來手動清除該多義性。若要恰當編譯下面的示例而不發生多義性,請應用以下代碼:

C *pc = new C;

pc->B::a();

留意
在聲明 C 時,假如在 B 的規模內援用 C,則能夠會招致湧現毛病。但不會收回任何毛病,直到在 B 的規模內現實創立對 C 的非限制援用。
主導
經由過程一個繼續關系圖達到多個稱號(函數、對象或列舉器)是能夠的。這類情形被視為與非虛擬基類一路應用時目標不明白。這些稱號與虛擬基類一路應用時目標不明白,除非個中一個稱號“決議”其他稱號。
假如某個稱號在兩個類中界說而且一個類派生自另外一個類,則該稱號可掌握另外一個稱號。基准稱號是派生類中的稱號;此稱號在本應湧現多義性時應用,如以下示例所示:

// deriv_Dominance.cpp
// compile with: /LD
class A {
public:
 int a;
};

class B : public virtual A {
public:
 int a();
};

class C : public virtual A {};

class D : public B, public C {
public:
 D() { a(); } // Not ambiguous. B::a() dominates A::a.
};

不明白的轉換
從指向類類型的指針或對類類型的援用的顯式或隱式轉換能夠會招致多義性。下圖(指向基類的指針的不明白轉換)顯示以下內容:
D 類型的對象的聲明。
將 address-of 運算符 (&) 運用於該對象的後果。請留意,address-of 運算符老是供給該對象的基址。
將應用 address-of 運算符獲得的指針顯式轉換為基類類型 A 的後果。請留意,將該對象的地址強迫轉換為 A* 類型其實不老是為編譯器供給足夠的信息,以供 A 類型的子對象停止選擇;在這類情形下,將存在兩個子對象。

指針到基類的不明白轉換
到類型 A*(指向 A 的指針)的轉換是不明白的,由於沒法辯明 A 類型的哪一個子對象是准確的。請留意,您可以經由過程顯式指定要應用的子對象來防止多義性,以下所示:

(A *)(B *)&d  // Use B subobject.
(A *)(C *)&d  // Use C subobject.

多義性和虛擬基類
假如應用虛擬基類,則函數、對象、類型和列舉數可經由過程多重繼續途徑達到。由於唯一一個基類實例,是以在拜訪這些稱號時不存在二義性。
下圖顯示若何應用虛擬和非虛擬繼續組成對象。

虛擬和非虛擬派生
在該圖中,經由過程非虛擬基類拜訪類 A 的任何成員都將招致二義性;編譯器沒有說明是應用與 B 聯系關系的子對象照樣與 C 聯系關系的子對象的信息。然則,將 A 指定為虛擬基類時,拜訪哪個子對象都不成成績。

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