解析C++編程中virtual聲明的虛函數和單個繼續。本站提示廣大學習愛好者:(解析C++編程中virtual聲明的虛函數和單個繼續)文章只能為提供參考,不一定能成為您想要的結果。以下是解析C++編程中virtual聲明的虛函數和單個繼續正文
虛函數
虛函數是應在派生類中從新界說的成員函數。 當應用指針或對基類的援用來援用派生的類對象時,可認為該對象挪用虛函數並履行該函數的派生類版本。
虛函數確保為該對象挪用准確的函數,這與用於停止函數挪用的表達式有關。
假定基類包括聲明為 virtual 的函數,而且派生類界說了雷同的函數。 為派生類的對象挪用派生類中的函數,即便它是應用指針或對基類的援用來挪用的。 以下示例顯示了一個基類,它供給了 PrintBalance 函數和兩個派生類的完成
// deriv_VirtualFunctions.cpp // compile with: /EHsc #include <iostream> using namespace std; class Account { public: Account( double d ) { _balance = d; } virtual double GetBalance() { return _balance; } virtual void PrintBalance() { cerr << "Error. Balance not available for base type." << endl; } private: double _balance; }; class CheckingAccount : public Account { public: CheckingAccount(double d) : Account(d) {} void PrintBalance() { cout << "Checking account balance: " << GetBalance() << endl; } }; class SavingsAccount : public Account { public: SavingsAccount(double d) : Account(d) {} void PrintBalance() { cout << "Savings account balance: " << GetBalance(); } }; int main() { // Create objects of type CheckingAccount and SavingsAccount. CheckingAccount *pChecking = new CheckingAccount( 100.00 ) ; SavingsAccount *pSavings = new SavingsAccount( 1000.00 ); // Call PrintBalance using a pointer to Account. Account *pAccount = pChecking; pAccount->PrintBalance(); // Call PrintBalance using a pointer to Account. pAccount = pSavings; pAccount->PrintBalance(); }
在後面的代碼中,對 PrintBalance 的挪用是雷同的,pAccount 所指向的對象除外。 因為 PrintBalance 是虛擬的,是以將挪用為每一個對象界說的函數版本。 派生類 PrintBalance 和 CheckingAccount 中的 SavingsAccount 函數“重寫”基類 Account 中的函數。
假如聲明的類不供給 PrintBalance 函數的重寫完成,則應用基類 Account 中的默許完成。
派生類中的函數僅在基類中的虛函數的類型雷同時重寫這些虛函數。 派生類中的函數不克不及只是與其前往類型中的基類的虛函數分歧;參數列表也必需分歧。
當應用指針或援用挪用函數時,以下規矩將實用:
依據為其挪用的對象的根本類型來解析對虛函數的挪用。
依據指針或援用的類型來解析對非虛函數的挪用。
以下示例解釋在經由過程指針挪用時虛函數和非虛函數的行動:
// deriv_VirtualFunctions2.cpp // compile with: /EHsc #include <iostream> using namespace std; class Base { public: virtual void NameOf(); // Virtual function. void InvokingClass(); // Nonvirtual function. }; // Implement the two functions. void Base::NameOf() { cout << "Base::NameOf\n"; } void Base::InvokingClass() { cout << "Invoked by Base\n"; } class Derived : public Base { public: void NameOf(); // Virtual function. void InvokingClass(); // Nonvirtual function. }; // Implement the two functions. void Derived::NameOf() { cout << "Derived::NameOf\n"; } void Derived::InvokingClass() { cout << "Invoked by Derived\n"; } int main() { // Declare an object of type Derived. Derived aDerived; // Declare two pointers, one of type Derived * and the other // of type Base *, and initialize them to point to aDerived. Derived *pDerived = &aDerived; Base *pBase = &aDerived; // Call the functions. pBase->NameOf(); // Call virtual function. pBase->InvokingClass(); // Call nonvirtual function. pDerived->NameOf(); // Call virtual function. pDerived->InvokingClass(); // Call nonvirtual function. }
輸入
Derived::NameOf Invoked by Base Derived::NameOf Invoked by Derived
請留意,不管 NameOf 函數是經由過程指向 Base 的指針照樣經由過程指向 Derived 的指針停止挪用,它都邑挪用 Derived 的函數。 它挪用 Derived 的函數,由於 NameOf 是虛函數,而且 pBase 和 pDerived 都指向類型 Derived 的對象。
因為僅為類類型的對象挪用虛函數,是以不克不及將全局函數或靜態函數聲明為 virtual。
在派生類中聲明重寫函數時可以使用 virtual 症結字,但它不是必須的;虛函數的重寫一直是虛擬的。
必需界說基類中的虛函數,除非應用 pure-specifier 聲明它們。 (有關純虛函數的具體信息,請參閱籠統類。)
可經由過程應用規模解析運算符 (::) 顯式限制函數稱號來禁用虛函數挪用機制。 斟酌先前觸及 Account 類的示例。 若要挪用基類中的 PrintBalance,請應用以下所示的代碼:
CheckingAccount *pChecking = new CheckingAccount( 100.00 ); pChecking->Account::PrintBalance(); // Explicit qualification. Account *pAccount = pChecking; // Call Account::PrintBalance pAccount->Account::PrintBalance(); // Explicit qualification.
在後面的示例中,對 PrintBalance 的挪用將禁用虛函數挪用機制。
單個繼續
在“單繼續”(繼續的罕見情勢)中,類僅具有一個基類。斟酌下圖中闡釋的關系。
簡略單繼續關系圖
留意該圖中從慣例到特定的進度。在年夜多半類條理構造的設計中發明的另外一個罕見特征是,派生類與基類具有“某種”關系。在該圖中,Book 是一種 PrintedDocument,而 PaperbackBook 是一種 book。
該圖中的另外一個要留意的是:Book 既是派生類(來自 PrintedDocument),又是基類(PaperbackBook 派生自 Book)。此類類條理構造的框架聲明以下面的示例所示:
// deriv_SingleInheritance.cpp // compile with: /LD class PrintedDocument {}; // Book is derived from PrintedDocument. class Book : public PrintedDocument {}; // PaperbackBook is derived from Book. class PaperbackBook : public Book {};
PrintedDocument 被視為 Book 的“直接基”類;它是 PaperbackBook 的“直接基”類。差別在於,直接基類湧現在類聲明的基本列表中,而直接基類不是如許的。
在聲明派生的類之前聲明從中派生每一個類的基類。為基類供給前向援用聲明是不敷的;它必需是一個完全聲明。
在後面的示例中,應用拜訪解釋符 public。 成員拜訪掌握中引見了公共的、受掩護的和公有的繼續的寄義。
類可用作多個特定類的基類,以下圖所示。
留意
有向非輪回圖關於單繼續不是獨一的。它們還用於表現多重繼續關系圖。 多重繼續中對本主題停止了解釋。
在繼續中,派生類包括基類的成員和您添加的一切新成員。是以,派生類可以援用基類的成員(除非在派生類中從新界說這些成員)。當在派生類中從新界說了直接或直接基類的成員時,規模解析運算符 (::) 可用於援用這些成員。請看以下示例:
// deriv_SingleInheritance2.cpp // compile with: /EHsc /c #include <iostream> using namespace std; class Document { public: char *Name; // Document name. void PrintNameOf(); // Print name. }; // Implementation of PrintNameOf function from class Document. void Document::PrintNameOf() { cout << Name << endl; } class Book : public Document { public: Book( char *name, long pagecount ); private: long PageCount; }; // Constructor from class Book. Book::Book( char *name, long pagecount ) { Name = new char[ strlen( name ) + 1 ]; strcpy_s( Name, strlen(Name), name ); PageCount = pagecount; };
請留意,Book 的結構函數 (Book::Book) 具有對數據成員 Name 的拜訪權。在法式中,可以創立和應用類型為 Book 的對象,以下所示:
// Create a new object of type Book. This invokes the // constructor Book::Book. Book LibraryBook( "Programming Windows, 2nd Ed", 944 ); ... // Use PrintNameOf function inherited from class Document. LibraryBook.PrintNameOf();
如後面的示例所示,以雷同的方法應用類成員和繼續的數據和函數。假如類 Book 的完成挪用 PrintNameOf 函數的從新完成,則只能經由過程應用規模解析 (Document) 運算符來挪用屬於 :: 類的函數:
// deriv_SingleInheritance3.cpp // compile with: /EHsc /LD #include <iostream> using namespace std; class Document { public: char *Name; // Document name. void PrintNameOf() {} // Print name. }; class Book : public Document { Book( char *name, long pagecount ); void PrintNameOf(); long PageCount; }; void Book::PrintNameOf() { cout << "Name of book: "; Document::PrintNameOf(); }
假如存在可拜訪的明白基類,則可以隱式將派生類的指針和援用轉換為其基類的指針和援用。上面的代碼應用指針演示了此概念(雷同的准繩實用於援用):
// deriv_SingleInheritance4.cpp // compile with: /W3 struct Document { char *Name; void PrintNameOf() {} }; class PaperbackBook : public Document {}; int main() { Document * DocLib[10]; // Library of ten documents. for (int i = 0 ; i < 10 ; i++) DocLib[i] = new Document; }
在後面的示例中,創立了分歧的類型。然則,因為這些類型都派生自 Document 類,是以存在對 Document * 的隱式轉換。是以,DocLib 是“異類列表”(個中包括的一切對象並不是屬於統一類型),該列表包括分歧類型的對象。
因為 Document 類具有一個 PrintNameOf 函數,是以它可以打印庫中每本書的稱號,但它能夠會疏忽某些特定於文檔類型的信息(Book 的頁計數、HelpFile 的字節數等)。
留意
強迫應用基類來完成函數(如 PrintNameOf)平日不是最好設計。 虛函數供給其他設計替換辦法。