1. 程式人生 > >C++ 中 虛擬繼承 的概念

C++ 中 虛擬繼承 的概念

C++中虛擬繼承的概念

為了解決從不同途徑繼承來的同名的資料成員在記憶體中有不同的拷貝造成資料不一致問題,將共同基類設定為虛基類。這時從不同的路徑繼承過來的同名數據成員在記憶體中就只有一個拷貝,同一個函式名也只有一個對映。這樣不僅就解決了二義性問題,也節省了記憶體,避免了資料不一致的問題。
class 派生類名:virtual 繼承方式 基類名
virtual是關鍵字,宣告該基類為派生類的虛基類。
在多繼承情況下,虛基類關鍵字的作用範圍和繼承方式關鍵字相同,只對緊跟其後的基類起作用。
聲明瞭虛基類之後,虛基類在進一步派生過程中始終和派生類一起,維護同一個基類子物件的拷貝。

C++虛擬繼承

◇概念:

C++使用虛擬繼承(Virtual Inheritance),解決從不同途徑繼承來的同名的資料成員在記憶體中有不同的拷貝造成資料不一致問題,將共同基類設定為虛基類。這時從不同的路徑繼承過來的同名數據成員在記憶體中就只有一個拷貝,同一個函式名也只有一個對映。

◇解決問題:

解決了二義性問題,也節省了記憶體,避免了資料不一致的問題。
◇同義詞: 
虛基類(把一個動詞當成一個名詞而已)
當在多條繼承路徑上有一個公共的基類,在這些路徑中的某幾條匯合處,這個公共的基類就會產生多個例項(或多個副本),若只想儲存這個基類的一個例項,可以將這個公共基類說明為虛基類。

◇語法:

class 派生類: virtual 基類1,virtual 基類2,...,virtual 基類n

{

...//派生類成員宣告

};

◇執行順序

首先執行虛基類的建構函式,多個虛基類的建構函式按照被繼承的順序構造;

執行基類的建構函式,多個基類的建構函式按照被繼承的順序構造;

執行成員物件的建構函式,多個成員物件的建構函式按照申明的順序構造;

執行派生類自己的建構函式;

析構以與構造相反的順序執行;

mark

從虛基類直接或間接派生的派生類中的建構函式的成員初始化列表中都要列出對虛基類建構函式的呼叫。但只有用於建立物件的最派生類的建構函式呼叫虛基類的建構函式,而該派生類的所有基類中列出的對虛基類的建構函式的呼叫在執行中被忽略,從而保證對虛基類子物件只初始化一次。

在一個成員初始化列表中同時出現對虛基類和非虛基類建構函式的呼叫時,虛基類的建構函式先於非虛基類的建構函式執行。

◇因果:

多重繼承->二義性->虛擬繼承解決

◇二義性:

1: //-----------------------------------------------------

 2: //名稱:blog_virtual_inherit.cpp 
 3: //說明:C++虛擬繼承學習演示 
 4: //環境:VS2005 
 5: //blog:pppboy.blog.163.com 
 6: //---------------------------------------------------- 
 7: #include "stdafx.h"
 8: #include <iostream>
 9: using namespace std;
 10:
 11: //Base 
 12: class Base
 13: {
 14: public:
 15: Base(){cout << "Base called..."<< endl;}
 16: void print(){cout << "Base print..." <<endl;}
 17: private:
 18: };
 19:
 20: //Sub 
 21: class Sub //定義一個類 Sub 
 22: {
 23: public:
 24: Sub(){cout << "Sub called..." << endl;}
 25: void print(){cout << "Sub print..." << endl;}
 26: private:
 27: };
 28:
 29: //Child 
 30: class Child : public Base , public Sub //定義一個類Child 分別繼承自 Base ,Sub 
 31: {
 32: public:
 33: Child(){cout << "Child called..." << endl;}
 34: private:
 35: };
 36:
 37: int main(int argc, char* argv[])
 38: {
 39: Child c;
 40:
 41: //不能這樣使用,會產生二意性,VC下error C2385 
 42: //c.print();  
 43:
 44: //只能這樣使用 
 45: c.Base::print();
 46: c.Sub::print();
 47:
 48: system("pause");
 49: return 0;
 50: }

◇多重繼承:

 1: //-----------------------------------------------------  
 2: //名稱:blog_virtual_inherit.cpp  
 
3: //說明:C++虛擬繼承學習演示  
 4: //環境:VS2005  
 5: //blog:pppboy.blog.163.com  
 6: //----------------------------------------------------  
 7: #include "stdafx.h"  
 8: #include <iostream>
 9: using namespace std;  
 10:
 11: int gFlag = 0;  
 12:
 13: class Base  
 14: {
 15: public:  
 16: Base(){cout << "Base called : " << gFlag++ << endl;}  
 17: void print(){cout << "Base print" <<endl;}  
 18: };
 19:
 20: class Mid1 : public Base  
 21: {
 22: public:  
 23: Mid1(){cout << "Mid1 called" << endl;}  
 24: private:  
 25: };
 26:
 27: class Mid2 : public Base  
 28: {
 29: public:  
 30: Mid2(){cout << "Mid2 called" << endl;}  
 31: };
 32:
 33: class Child:public Mid1, public Mid2  
 34: {
 35: public:  
 36: Child(){cout << "Child called" << endl;}  
 37: };
 38:
 39: int main(int argc, char* argv[])  
 40: {
 41: Child d;
 42:
 43: //不能這樣使用,會產生二意性  
     //d.print();  
 45:
 46: //只能這樣使用  
 47: d.Mid1::print();
 48: d.Mid2::print();
 49:
 50: system("pause");  
 51: return 0;  
 52: }
 53:

//output

 Base called : 0
 Mid1 called
 Base called : 1 
 Mid2 called
 Child called
 Base print
 Base print

◇虛擬繼承

在派生類繼承基類時,加上一個virtual關鍵詞則為虛擬繼承

 1: //-----------------------------------------------------  
 2: //名稱:blog_virtual_inherit.cpp  
 
3: //說明:C++虛擬繼承學習演示  
 4: //環境:VS2005  
 5: //blog:pppboy.blog.163.com  
 6: //----------------------------------------------------  
 7: #include "stdafx.h"  
 8: #include <iostream>
 9: using namespace std;  
 10:
 11: int gFlag = 0;  
 12:
 13: class Base  
 14: {
 15: public:  
 16: Base(){cout << "Base called : " << gFlag++ << endl;}  
 17: void print(){cout << "Base print" <<endl;}  
 18: };
 19:
 20: class Mid1 :virtualpublic Base  
 21: {
 22: public:  
 23: Mid1(){cout << "Mid1 called" << endl;}  
 24: private:  
 25: };
 26:
 27: class Mid2 : virtual public Base  
 28: {
 29: public:  
 30: Mid2(){cout << "Mid2 called" << endl;}  
 31: };
 32:
 33: class Child:public Mid1, public Mid2  
 34: {
 35: public:  
 36: Child(){cout << "Child called" << endl;}  
 37: };
 38:
 39: int main(int argc, char* argv[])  
 40: {
 41: Child d;
 42:
 43: //這裡可以這樣使用  
 44: d.print();
 45:
 46: //也可以這樣使用  
 47: d.Mid1::print();
 48: d.Mid2::print();
 49:
 50: system("pause");  
 51: return 0;  
 52: }
 53:

//output

 1: Base called : 0
 2: Mid1 called
 3: Mid2 called
 4: Child called
 5: Base print
 6: Base print
 7: Base print
 8: 請按任意鍵繼續. . .

◇通過輸出的比較

1.在多繼承情況下,虛基類關鍵字的作用範圍和繼承方式關鍵字相同,只對緊跟其後的基類起作用。
2.聲明瞭虛基類之後,虛基類在進一步派生過程中始終和派生類一起,維護同一個基類子物件的拷貝。
3.觀察類建構函式的構造順序,拷貝也只有一份。
◇與虛擬函式關係 
虛擬繼承與虛擬函式有一定相似的地方,但他們之間是絕對沒有任何聯絡的。
再想一次:虛擬繼承,虛基類,虛擬函式。

相關推薦

C++ 虛擬繼承概念

C++中虛擬繼承的概念 為了解決從不同途徑繼承來的同名的資料成員在記憶體中有不同的拷貝造成資料不一致問題,將共同基類設定為虛基類。這時從不同的路徑繼承過來的同名數據成員在記憶體中就只有一個拷貝,同一個函式名也只有一個對映。這樣不僅就解決了二義性問題,也節省了記憶體,避免了資

C++虛擬函式工作原理和 虛 繼承類的記憶體佔用大小計算

                      虛擬函式的實現要求物件攜帶額外的資訊,這些資訊用於在執行時確定該物件應該呼叫哪一個虛擬函式。典型情況下,這一資訊具有一種被稱為 vptr(virtual table pointer,虛擬函式表指標)的指標的形式。vptr 指向一個被稱為 vtbl(virtual t

C++繼承與多重繼承下的虛擬函式表

轉自:http://www.cnblogs.com/Z465360621/articles/4561344.html 虛擬函式表,以及虛擬函式指標: 1)每個有虛擬函式的類都有自己的虛擬函式表,每個包含虛擬函式的類物件都有虛擬函式表指標。 2)對於多重繼承

C#繼承與覆蓋

sta 文章 class static color read con public ner 原文發布時間為:2009-03-03 —— 來源於本人的百度文章 [由搬家工具導入]//using System;//using System.Collections.Generic

C++繼承詳解

C++ 繼承 [TOC] 繼承基本知識 定義:  繼承是面向對復用的重要手段。通過繼承定義一個類,繼承是類型之間的關系建模,共享公有的東西,實現各自本質不同的東西。 繼承關系:  三種繼承關系下基類成員的在派生類的訪問關系變化(圖) 舉個栗子(公有繼承) ```c+

關於C++繼承和過載的區別

        C++中的很多特性光從概念上的話,很難做區分。或者說,概念讓人容易模糊,比如說函式過載和函式繼承。        先說過載,過載分為操作符過載和函式名過載,其中,操作符過載就是對運算操作符的原有功能進

c++虛擬函式的理解

虛擬函式的作用,事實上就是實現了多型性,就是實現以共同的方法,但因個體差異而採用不同的策略。下面有程式碼例項來描述: class A{ public: void print(){ cout<<”This is A”<<endl;} }; class B:publ

C++虛擬函式與函式

解構函式為什麼要宣告為虛 函式??? 基類的解構函式需要宣告為虛擬函式:  當派生類物件經由一個基類指標被刪除,而該基類帶著一個non-virtual解構函式,實際執行時通常發生的是物件的派生類成員沒有被銷燬。這也就是區域性銷燬,會發生記憶體洩漏,所以我們通常將基類的解構函式需要宣告為

C++繼承和組合區別使用

C++的“繼承”特性可以提高程式的可複用性。正因為“繼承”太有用、太容易用,才要防止亂用“繼承”。我們要給“繼承”立一些使用規則:   一、如果類A 和類B 毫不相關,不可以為了使B 的功能更多些而讓B 繼承A 的功能。   不要覺得“

C++虛擬函式工作原理

C++中的虛擬函式的作用主要是實現了多型的機制。關於多型,簡而言之就是用父類型別的指標指向其子類的例項,然後通過父類的指標呼叫實際子類的成員函式。 所謂泛型技術,比如:模板技術,RTTI技術,虛擬函式技術,要麼是試圖做到在編譯時決議,要麼試圖做到執行時決議。 虛擬函式表(

原來Java繼承C#繼承是不一樣的

因為之前在學校裡學的是Java,C#是自學的,一直感覺Java和C#就像是孿生兄弟,不過今天才發現原來Java中的繼承和C#中的繼承還是有點不一樣的 在Java中子類繼承父類的方法,如果在子類重新定義了父類方法的實現,那麼這個過程就是重寫,且只能是重寫,而在C

C++三大繼承方式的執行效果

#include <iostream> using namespace std; class A { public: void inial(){}; int size; protected: int val; private: int pr

c++虛擬函式和純虛擬函式定義

      只有用virtual宣告類的成員函式,使之成為虛擬函式,不能將類外的普通函式宣告為虛擬函式。因為虛擬函式的作用是允許在派生類中對基類的虛擬函式重新定義。所以虛擬函式只能用於類的繼承層次結構中。      一個成員函式被宣告為虛擬函式後,在同一類族中的類就不能

關於c++虛擬函式和介面的關係區分(簡單)

虛擬函式:                 虛擬函式的作用是實現動態聯編,也就是在程式的執行階段動態地選擇合適的成員函式,在定義了虛擬函式後,可以在基類的派生類中對虛擬函式重新定義,在派生類中重新定義的函式應與虛擬函式具有相同的形參個數和形參型別。以實現統一的介面,不同定義

C++繼承建構函式呼叫順序

class B1 {public: B1(int i) {cout<<"consB1"<<i<<endl;} };//定義基類B1 class B2 {public: B2(int j) {cout<<"consB2"<<

C++虛擬函式表儲存位置淺析

關於C++中虛擬函式表,我們知道這樣一些事實: 1. 當class中存在virtual函式時,編譯器會為這個class追加一個void** __vfptr資料成員。 2. C++程式執行時,實際函式的呼叫,是通過查詢__vfptr來獲取的,從而實現多型。 3. 多型的實現,

C#繼承和多型

繼承的作用: 繼承可以減少程式碼的冗餘,提高程式碼的複用性。 是面向物件程式設計的組成部分,是所有面向物件的語言都要具備的特性。 繼承的實現: 先將幾個類中相同的屬性和方法進行抽離,單獨定義在一個新建的類中。 其他的類直接繼承當前新建的

淺析C++虛擬函式的呼叫及物件的內部佈局

     在我那篇《淺析C++中的this指標》中,我通過分析C++程式碼編譯後生成的彙編程式碼來分析this指標的實現方法。這次我依然用分析C++程式碼編譯後生成的彙編程式碼來說明C++中虛擬函式呼叫的實現方法,順便也說明一下C++中的物件內部佈局。下面所有的彙編程式碼都是

C++繼承特性(1)

1.何為繼承 首先,繼承是什麼?繼承可以簡單理解為孩子繼承了父母雙親的基因,在擁有父母某些特性的同時,又有自己獨立的特性。 在C++中,繼承是類與類之間的繼承,即某個類可以繼承它類的成員變數、成員函

C++虛擬函式的作用是什麼?它應該怎麼用呢?

虛擬函式聯絡到多型,多型聯絡到繼承。所以本文中都是在繼承層次上做文章。沒了繼承,什麼都沒得談。下面是對C++的虛擬函式這玩意兒的理解。 一, 什麼是虛擬函式 (如果不知道虛擬函式為何物,但有急切的想知道,那你就應該從這裡開始)簡單地說,那些被virtual關鍵字修飾的成員