解析C++編程中virtual聲明的虛函數(shù)以及單個(gè)繼承
虛函數(shù)
虛函數(shù)是應(yīng)在派生類中重新定義的成員函數(shù)。 當(dāng)使用指針或?qū)惖囊脕?lái)引用派生的類對(duì)象時(shí),可以為該對(duì)象調(diào)用虛函數(shù)并執(zhí)行該函數(shù)的派生類版本。
虛函數(shù)確保為該對(duì)象調(diào)用正確的函數(shù),這與用于進(jìn)行函數(shù)調(diào)用的表達(dá)式無(wú)關(guān)。
假定基類包含聲明為 virtual 的函數(shù),并且派生類定義了相同的函數(shù)。 為派生類的對(duì)象調(diào)用派生類中的函數(shù),即使它是使用指針或?qū)惖囊脕?lái)調(diào)用的。 以下示例顯示了一個(gè)基類,它提供了 PrintBalance 函數(shù)和兩個(gè)派生類的實(shí)現(xiàn)
// 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(); }
在前面的代碼中,對(duì) PrintBalance 的調(diào)用是相同的,pAccount 所指向的對(duì)象除外。 由于 PrintBalance 是虛擬的,因此將調(diào)用為每個(gè)對(duì)象定義的函數(shù)版本。 派生類 PrintBalance 和 CheckingAccount 中的 SavingsAccount 函數(shù)“重寫(xiě)”基類 Account 中的函數(shù)。
如果聲明的類不提供 PrintBalance 函數(shù)的重寫(xiě)實(shí)現(xiàn),則使用基類 Account 中的默認(rèn)實(shí)現(xiàn)。
派生類中的函數(shù)僅在基類中的虛函數(shù)的類型相同時(shí)重寫(xiě)這些虛函數(shù)。 派生類中的函數(shù)不能只是與其返回類型中的基類的虛函數(shù)不同;參數(shù)列表也必須不同。
當(dāng)使用指針或引用調(diào)用函數(shù)時(shí),以下規(guī)則將適用:
根據(jù)為其調(diào)用的對(duì)象的基本類型來(lái)解析對(duì)虛函數(shù)的調(diào)用。
根據(jù)指針或引用的類型來(lái)解析對(duì)非虛函數(shù)的調(diào)用。
以下示例說(shuō)明在通過(guò)指針調(diào)用時(shí)虛函數(shù)和非虛函數(shù)的行為:
// 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
請(qǐng)注意,無(wú)論 NameOf 函數(shù)是通過(guò)指向 Base 的指針還是通過(guò)指向 Derived 的指針進(jìn)行調(diào)用,它都會(huì)調(diào)用 Derived 的函數(shù)。 它調(diào)用 Derived 的函數(shù),因?yàn)?NameOf 是虛函數(shù),并且 pBase 和 pDerived 都指向類型 Derived 的對(duì)象。
由于僅為類類型的對(duì)象調(diào)用虛函數(shù),因此不能將全局函數(shù)或靜態(tài)函數(shù)聲明為 virtual。
在派生類中聲明重寫(xiě)函數(shù)時(shí)可使用 virtual 關(guān)鍵字,但它不是必需的;虛函數(shù)的重寫(xiě)始終是虛擬的。
必須定義基類中的虛函數(shù),除非使用 pure-specifier 聲明它們。 (有關(guān)純虛函數(shù)的詳細(xì)信息,請(qǐng)參閱抽象類。)
可通過(guò)使用范圍解析運(yùn)算符 (::) 顯式限定函數(shù)名稱來(lái)禁用虛函數(shù)調(diào)用機(jī)制。 考慮先前涉及 Account 類的示例。 若要調(diào)用基類中的 PrintBalance,請(qǐng)使用如下所示的代碼:
CheckingAccount *pChecking = new CheckingAccount( 100.00 ); pChecking->Account::PrintBalance(); // Explicit qualification. Account *pAccount = pChecking; // Call Account::PrintBalance pAccount->Account::PrintBalance(); // Explicit qualification.
在前面的示例中,對(duì) PrintBalance 的調(diào)用將禁用虛函數(shù)調(diào)用機(jī)制。
單個(gè)繼承
在“單繼承”(繼承的常見(jiàn)形式)中,類僅具有一個(gè)基類??紤]下圖中闡釋的關(guān)系。
簡(jiǎn)單單繼承關(guān)系圖
注意該圖中從常規(guī)到特定的進(jìn)度。在大多數(shù)類層次結(jié)構(gòu)的設(shè)計(jì)中發(fā)現(xiàn)的另一個(gè)常見(jiàn)特性是,派生類與基類具有“某種”關(guān)系。在該圖中,Book 是一種 PrintedDocument,而 PaperbackBook 是一種 book。
該圖中的另一個(gè)要注意的是:Book 既是派生類(來(lái)自 PrintedDocument),又是基類(PaperbackBook 派生自 Book)。此類類層次結(jié)構(gòu)的框架聲明如下面的示例所示:
// 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 的“間接基”類。差異在于,直接基類出現(xiàn)在類聲明的基礎(chǔ)列表中,而間接基類不是這樣的。
在聲明派生的類之前聲明從中派生每個(gè)類的基類。為基類提供前向引用聲明是不夠的;它必須是一個(gè)完整聲明。
在前面的示例中,使用訪問(wèn)說(shuō)明符 public。 成員訪問(wèn)控制中介紹了公共的、受保護(hù)的和私有的繼承的含義。
類可用作多個(gè)特定類的基類,如下圖所示。
注意
有向非循環(huán)圖對(duì)于單繼承不是唯一的。它們還用于表示多重繼承關(guān)系圖。 多重繼承中對(duì)本主題進(jìn)行了說(shuō)明。
在繼承中,派生類包含基類的成員以及您添加的所有新成員。因此,派生類可以引用基類的成員(除非在派生類中重新定義這些成員)。當(dāng)在派生類中重新定義了直接或間接基類的成員時(shí),范圍解析運(yùn)算符 (::) 可用于引用這些成員。請(qǐng)看以下示例:
// 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; };
請(qǐng)注意,Book 的構(gòu)造函數(shù) (Book::Book) 具有對(duì)數(shù)據(jù)成員 Name 的訪問(wèn)權(quán)。在程序中,可以創(chuàng)建和使用類型為 Book 的對(duì)象,如下所示:
// 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();
如前面的示例所示,以相同的方式使用類成員和繼承的數(shù)據(jù)和函數(shù)。如果類 Book 的實(shí)現(xiàn)調(diào)用 PrintNameOf 函數(shù)的重新實(shí)現(xiàn),則只能通過(guò)使用范圍解析 (Document) 運(yùn)算符來(lái)調(diào)用屬于 :: 類的函數(shù):
// 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(); }
如果存在可訪問(wèn)的明確基類,則可以隱式將派生類的指針和引用轉(zhuǎn)換為其基類的指針和引用。下面的代碼使用指針演示了此概念(相同的原則適用于引用):
// 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; }
在前面的示例中,創(chuàng)建了不同的類型。但是,由于這些類型都派生自 Document 類,因此存在對(duì) Document * 的隱式轉(zhuǎn)換。因此,DocLib 是“異類列表”(其中包含的所有對(duì)象并非屬于同一類型),該列表包含不同類型的對(duì)象。
由于 Document 類具有一個(gè) PrintNameOf 函數(shù),因此它可以打印庫(kù)中每本書(shū)的名稱,但它可能會(huì)忽略某些特定于文檔類型的信息(Book 的頁(yè)計(jì)數(shù)、HelpFile 的字節(jié)數(shù)等)。
注意
強(qiáng)制使用基類來(lái)實(shí)現(xiàn)函數(shù)(如 PrintNameOf)通常不是最佳設(shè)計(jì)。 虛函數(shù)提供其他設(shè)計(jì)替代方法。
上一篇:結(jié)合C++11的新特性來(lái)解析C++中的枚舉與聯(lián)合
欄 目:C語(yǔ)言
下一篇:詳解C++中的函數(shù)調(diào)用和下標(biāo)以及成員訪問(wèn)運(yùn)算符的重載
本文標(biāo)題:解析C++編程中virtual聲明的虛函數(shù)以及單個(gè)繼承
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/2534.html
您可能感興趣的文章
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)- 解析最少換車(chē)次數(shù)的問(wèn)題詳解
- 01-10深入理解C++中常見(jiàn)的關(guān)鍵字含義
- 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
- 01-10深入解析最長(zhǎng)公共子串
- 01-10c++中inline的用法分析
- 01-10用C++實(shí)現(xiàn)DBSCAN聚類算法
- 01-10全排列算法的非遞歸實(shí)現(xiàn)與遞歸實(shí)現(xiàn)的方法(C++)
- 01-10C++大數(shù)模板(推薦)
- 01-10淺談C/C++中的static與extern關(guān)鍵字的使用詳解


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫(xiě)分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫(xiě)函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 04-02jquery與jsp,用jquery
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-10C#中split用法實(shí)例總結(jié)
- 01-10delphi制作wav文件的方法
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?