1. 程式人生 > >C++學習:虛擬函式,純虛擬函式(virtual),虛繼承,虛解構函式

C++學習:虛擬函式,純虛擬函式(virtual),虛繼承,虛解構函式

C++學習:虛擬函式,虛繼承,純虛擬函式(virtual)虛解構函式

  1. 虛擬函式
  2. 純虛擬函式
  3. 虛解構函式
  4. 虛繼承

簡介
在java這種高階語言中,有abstract和interface這兩個關鍵字.代表的是抽象類和介面,但是在C++這門語言中,並沒有專屬的關鍵字來表示抽象類或者介面,但是它卻也有介面和抽象類這樣的概念.所以在這裡引入了vitural這個關鍵字,虛構的,虛有的意思.
因為C++在編譯的時候的專有特性,它在編譯的時候,一般的內部成員函式都是靜態載入的,因此也就在一般情況下不可能出現動態呼叫的過程,也就是我門在java中說的,父類出現的地方子類都可以出現.而vitural這個關鍵字的出現,多數使用的場景下是為了,能夠讓夠選擇性的去呼叫繼承來自父類並且重寫過的方法.
因此在本篇部落格也就引入了虛構這樣的一種概念.

提示:

虛構函式:

為什麼要有虛構函式??先看一組程式碼:

/*
 * ===========================================================================
 *
 *       Filename:  NomalFunctionExtends.cpp
 *    Description:  正常的繼承,父類為一般成員方法
 *        Version:  1.0
 *        Created:  2017年06月22日 21時42分43秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */
#include<iostream> using namespace::std; class Car{ public: Car(){ cout<<"Car consstructor"<<endl; } ~Car(){ cout<<"Car destructor"<<endl; } void start() const{ cout<<"car start"<<endl; } void stop() const{ cout
<<"cat stop"<<endl; } }; class Benz : public Car{ public: Benz(){ cout<<"Benz constructor"<<endl; } ~Benz(){ cout<<"Benz destructor"<<endl; } void start() const{ cout<<"Benz start"<<endl; } void stop() const{ cout<<"Benz stop"<<endl; } }; class Baoma:public Car{ public: Baoma(){ cout<<"Baoma constructor"<<endl; } ~Baoma(){ cout<<"Baoma destructor"<<endl; } void start() const{ cout<<"Baoma constructor"<<endl; } void stop() const{ cout<<"Baoma destructor"<<endl; } private: int speed; }; void carFunction(Car *car){ car->start(); car->stop(); } int main(int argc,char *argv[]){ Car *benz = new Benz(); cout<<sizeof(Benz)<<endl; carFunction(benz); Car *baoma = new Baoma(); cout<<sizeof(Baoma)<<endl; carFunction(baoma); delete benz; delete baoma; return 0; }

然後看輸出結果;

Car consstructor
Benz constructor
1  //內部沒有成員變數,因此只有一個位元組的空間
car start
cat stop
Car consstructor
Baoma constructor
4  //函式是不佔用記憶體的,baoma中有一個int型別.所以sizeof為4
car start
cat stop
Car destructor
Car destructor

注意:
C++編譯器不允許物件為零長度。試想一個長度為0的物件在記憶體中怎麼存放?怎麼獲取它的地址?為了避免這種情況,C++強制給這種類插入一個預設成員,長度為1。如果有自定義的變數,變數將取代這個預設成員
Benz和Baoma都是繼承自Car,根據裡式替換原則,父類能夠出現的地方,那麼子類也一定能夠出現.依賴抽象而不去依賴具體,在上述的函式呼叫過程中,我們傳進去的是benz和baoma指標.但是在呼叫函式的時候,它並沒有去呼叫子類的方法,這也就是一般成員函式的侷限性,就是在編譯的時候,一般性的函式已經被靜態的編譯進去,所以在呼叫的時候不能去選擇動態呼叫.

加入vitural關鍵字修飾的函式,將父類函式變為虛擬函式,看看變化

/*
 * ===========================================================================
 *
 *       Filename:  NomalFunctionExtends.cpp
 *    Description:  正常的繼承,父類為一般成員方法
 *        Version:  1.0
 *        Created:  2017年06月22日 21時42分43秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include<iostream>
using namespace::std;


class Car{
  public:
    Car(){
      cout<<"Car consstructor"<<endl;
    }
    ~Car(){
      cout<<"Car destructor"<<endl;
    }
    virtual void start() {
      cout<<"car start"<<endl;
    }
    virtual void stop() {
      cout<<"cat stop"<<endl;
    }
};

class Benz : public Car{

  public:
    Benz(){
      cout<<"Benz constructor"<<endl;
    }
    ~Benz(){
      cout<<"Benz destructor"<<endl;
    }
    //子類繼承父類,如果是虛擬函式,可以寫上vitural也可以不寫
    virtual void start() {
      cout<<"Benz start"<<endl;
    }
    void stop() {
      cout<<"Benz stop"<<endl;
    }
};


class Baoma:public Car{
   public:
     Baoma(){
       cout<<"Baoma constructor"<<endl;
     }
     ~Baoma(){
       cout<<"Baoma destructor"<<endl;
     }
     void start() {
        cout<<"Baoma start"<<endl;
     }
     void stop() {
       cout<<"Baoma stop"<<endl;
     }
    private:
     int speed;
};



void carFunction(Car *car){
  car->start();
  car->stop();
}

int main(int argc,char *argv[]){
  Car *benz = new Benz();
  cout<<sizeof(Benz)<<endl;
  carFunction(benz);

  Car *baoma = new Baoma();
  cout<<sizeof(Baoma)<<endl;
  carFunction(baoma);

  delete benz;
  delete baoma;

  return 0;
}

輸出結果:

    Car consstructor
    Benz constructor
    8
    Benz start
    Benz stop
    Car consstructor
    Baoma constructor
    16
    Baoma start
    Baoma stop
    Car destructor
    Car destructor

從上面的輸出結果中可以看到,加入了虛擬函式之後,呼叫不同指標物件指定函式的時候,這個時候都是去自動呼叫子類中的具體函式形式,而不是像一般函式的呼叫一樣,只是去呼叫父類的函式.這就是virtural關鍵字的作用,因為一般函式呼叫編譯的時候是靜態編譯的時候就已經決定了,加入了virtural的函式,一個類中函式的呼叫並不是在編譯的時候決定下來的,而是在執行時候被確定的,這也就是虛擬函式.

虛擬函式就是由於在由於編寫程式碼的時候並不能確定被呼叫的是基類的函式還是哪個派生類的函式,所以被 為“虛”函式。函式只能藉助於指標或者引用來達到多型的效果
注意點:

1:C++中的虛擬函式的作用主要是實現了多型的機制。關於多型,簡而言之就是用父類型別的指標指向其子類的例項,然後通過父類的指標呼叫實際子類的成員函式。
2:對C++ 瞭解的人都應該知道虛擬函式(Virtual Function)是通過一張虛擬函式表(Virtual Table)來實現的。簡稱為V-Table。在這個表中,主是要一個類的虛擬函式的地址表,這張表解決了繼承、覆蓋的問題,保證其容真實反應實際的函式。這樣,在有虛擬函式的類的例項中這個表被分配在了這個例項的記憶體中,所以,當我們用父類的指標來操作一個子類的時候,這張虛擬函式表就顯得由為重要了,它就像一個地圖一樣,指明瞭實際所應該呼叫的函式
3:帶有虛擬函式的物件自身確實插入了一些指標資訊,而且這個指標資訊並不隨著虛擬函式的增加而增大,這也就是為什麼上述增加了虛擬函式後,出現了size變大的現象

純虛擬函式

在C++中,如果一個變數使用const進行修飾,那麼就稱為常量,如果一個函式被const修飾,就被稱為常函式,那麼如果一個虛擬函式被const進行修飾呢??類似

    virtural void start() const;

在java中我們有一種類叫做抽象類,抽象類有一種特性,就是父類不能例項化,並且需要有abstract關鍵字進行修飾.但是在C++中同樣也有抽象函式,那麼在C++中抽象函式是怎麼去表示的,由於沒有抽象型別關鍵字,所以C++採用了virtural與const搭配的這種形式,構造出這樣一種抽象函式與抽象類.
注意:
抽象類是不能夠進行例項化的

/*
 * ===========================================================================
 *
 *       Filename:  NomalFunctionExtends.cpp
 *    Description:  抽象類
 *      抽象類的規定:
      1:帶有純虛擬函式的類叫做抽象類(注意與介面區分)
 *        2:抽象類只能作為其他類的基類,處於繼承層次的上層
 *        3:抽象類不能用作引數型別,函式返回型別,或者顯示轉換(抽象類不能夠進行例項化)
 *        4:可以定義抽象類的指標和引用,此指標指向它的派生類,
 *        5:抽象類一般既包括成員函式,又包括資料成員變數
 *        Version:  1.0
 *        Created:  2017年06月22日 21時42分43秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include<iostream>
using namespace::std;

/* *
 *將Car宣告定義成一個抽象類
 * */
class Car{
  public:
    Car(){
      cout<<"Car consstructor"<<endl;
    }
    ~Car(){
      cout<<"Car destructor"<<endl;
    }
    virtual void start() const = 0;  //抽象類必須包含純虛擬函式,並且在基類中不能定義,只能宣告
    virtual void stop() const = 0;
};

class Benz : public Car{

  public:
    Benz(){
      cout<<"Benz constructor"<<endl;
    }
    ~Benz(){
      cout<<"Benz destructor"<<endl;
    }
    //子類繼承父類,如果是虛擬函式,可以寫上vitural也可以不寫,重寫父類中的常虛擬函式
    virtual void start() const{
      cout<<"Benz start"<<endl;
    }
    void stop() const{
      cout<<"Benz stop"<<endl;
    }
};


class Baoma:public Car{
   public:
     Baoma(){
       cout<<"Baoma constructor"<<endl;
     }
     ~Baoma(){
       cout<<"Baoma destructor"<<endl;
     }
     void start() const{
        cout<<"Baoma start"<<endl;
     }
     void stop() const{
       cout<<"Baoma stop"<<endl;
     }
    private:
     int speed;
};



void carFunction(Car *car){
  car->start();
  car->stop();
}

int main(int argc,char *argv[]){
  //抽象類是不能夠進行例項化的,所以下面註釋掉的函式是錯誤的
  //Car *car = new Car();
  Car *benz = new Benz();  //父類的指標指向子類的成員
  cout<<sizeof(Benz)<<endl;
  carFunction(benz);

  Car *baoma = new Baoma();
  cout<<sizeof(Baoma)<<endl;
  carFunction(baoma);

  delete benz;
  delete baoma;
  //delete car;
  return 0;
}

輸出的結果為:

Car consstructor
Benz constructor
8
Benz start
Benz stop
Car consstructor
Baoma constructor
16
Baoma start
Baoma stop
Car destructor
Car destructor

注意:

  1:帶有純虛擬函式的類叫做抽象類(注意與介面區分)
  2:抽象類只能作為其他類的基類,處於繼承層次的上層
  3:抽象類不能用作引數型別,函式返回型別,或者顯示轉換(抽象類不能夠進行例項化)
  4:可以定義抽象類的指標和引用,此指標指向它的派生類,
  5:抽象類一般既包括成員函式,又包括資料成員變數

虛解構函式

在之前的部落格中,我們只知道什麼是解構函式,也就是在類被回收的時候,才會去呼叫的函式,它的作用就是在類回收的時候去做一些資源的釋放,但是什麼才是虛解構函式??跟虛擬函式有什麼關係??
一個類中如果有虛擬函式的話,那麼這個類的虛擬函式就支援動態執行函式過程.也就是說.在使用這個類的物件之前,並不確切的知道這個物件是指向哪個子類物件的,這也就造成了.在析構的時候,可能也並不知道析構的是誰的具體物件.

1:虛解構函式是為了解決基類的指標指向派生類物件,並用基類的指標刪除派生類物件。
2:如果某個類不包含虛擬函式,那一般是表示它將不作為一個基類來使用。當一個類不準備作為基類使用時,使解構函式為虛一般是個壞主意。因為它會為類增加一個虛擬函式表,使得物件的體積翻倍,還有可能降低其可移植性。
所以基本的一條是:無故的宣告虛解構函式和永遠不去宣告一樣是錯誤的。實際上,很多人這樣總結:當且僅當類裡包含至少一個虛擬函式的時候才去宣告虛解構函式。
3:抽象類是準備被用做基類的,基類必須要有一個虛解構函式,純虛擬函式會產生抽象類,所以方法很簡單:在想要成為抽象類的類裡宣告一個純虛解構函式

如果細心的看官會注意到,上面所有執行後的結果中,都沒有去析構Benz和Baoma的物件,而是僅僅析構的是基類Car的物件,如果資源沒有去進行回收的話,這樣的一種情況必然會導致記憶體洩露

對之前的程式碼進行修改.將父類的解構函式轉換稱虛解構函式

/*
 * ===========================================================================
 *
 *       Filename:  virtualConstructor.cpp
 *    Description:  虛解構函式
 *      抽象類的規定:
      1:帶有純虛擬函式的類叫做抽象類(注意與介面區分)
 *        2:抽象類只能作為其他類的基類,處於繼承層次的上層
 *        3:抽象類不能用作引數型別,函式返回型別,或者顯示轉換(抽象類不能夠進行例項化)
 *        4:可以定義抽象類的指標和引用,此指標指向它的派生類,
 *        5:抽象類一般既包括成員函式,又包括資料成員變數
 *        Version:  1.0
 *        Created:  2017年06月22日 21時42分43秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include<iostream>
using namespace::std;

/* *
 *將Car宣告定義成一個抽象類
 * */
class Car{
  public:
    Car(){
      cout<<"Car consstructor"<<endl;
    }
    virtual ~Car(){ //將解構函式變成虛解構函式,便於子類的解構函式也同時能夠回收
      cout<<"Car destructor"<<endl;
    }
    virtual void start() const = 0;  //抽象類必須包含純虛擬函式,並且在基類中不能定義,只能宣告
    virtual void stop() const = 0;
};

class Benz : public Car{

  public:
    Benz(){
      cout<<"Benz constructor"<<endl;
    }
    ~Benz(){
      cout<<"Benz destructor"<<endl;
    }
    //子類繼承父類,如果是虛擬函式,可以寫上vitural也可以不寫,重寫父類中的常虛擬函式
    virtual void start() const{
      cout<<"Benz start"<<endl;
    }
    void stop() const{
      cout<<"Benz stop"<<endl;
    }
};


class Baoma:public Car{
   public:
     Baoma(){
       cout<<"Baoma constructor"<<endl;
     }
     ~Baoma(){
       cout<<"Baoma destructor"<<endl;
     }
     void start() const{
        cout<<"Baoma start"<<endl;
     }
     void stop() const{
       cout<<"Baoma stop"<<endl;
     }
    private:
     int speed;
};



void carFunction(Car *car){
  car->start();
  car->stop();
}

int main(int argc,char *argv[]){
  //抽象類是不能夠進行例項化的,所以下面註釋掉的函式是錯誤的
  //Car *car = new Car();
  Car *benz = new Benz();  //父類的指標指向子類的成員
  cout<<sizeof(Benz)<<endl;
  carFunction(benz);

  Car *baoma = new Baoma();
  cout<<sizeof(Baoma)<<endl;
  carFunction(baoma);

  delete benz;
  delete baoma;
  //delete car;
  return 0;
}

輸出結果為:

 Car consstructor
Benz constructor
8
Benz start
Benz stop
Car consstructor
Baoma constructor
16
Baoma start
Baoma stop
Benz destructor
Car destructor
Baoma destructor
Car destructor

從上面的結果中可以看到,在物件析構的時候,同時也會分別去呼叫子類的解構函式,這也就是虛解構函式的意義

虛繼承

虛繼承 是面向物件程式設計中的一種技術,是指一個指定的基類,在繼承體系結構中,將其成員資料例項共享給也從這個基型別直接或間接派生的其它類。
虛擬繼承是多重繼承中特有的概念。虛擬基類是為解決多重繼承而出現的
簡而言之:
虛繼承就是為了解決多重繼承中,共享資料成員的使用而生的.

前一篇文章中,講到怎麼去解決多重繼承中,父類中同時包含一種變數的處理方法,可以通過父類來進行區分的方式,但是也有一種更有效的解決方法,就是將共有的變數抽取出來,作為頂層父類來處理,對之前的多重繼承案例進行更改優化

/*
 * ===========================================================================
 *
 *       Filename:  MultiInheritance.cpp
 *    Description:
 *    智慧手機是繼承了智慧裝置,現在的手機都能看電視,也繼承了功能手機的打電話,發簡訊的功能
 *        Version:  1.0
 *        Created:  2017年06月20日 22時05分41秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace::std;

/* *
 *
 *建立一個頂層的Size類
 * */
class Size{
  public:
    Size(){
      cout<<"Size constructor"<<endl;
    }
    ~Size(){
      cout<<"size destructor"<<endl;
    }
    void setWidth(int width){
      s_width = width; 
    }

   void setHeight(int height){
     s_height = height;
   }

   int getWidth() const{
     return s_width;
   }
   int getHeight() const{
     return s_height;
   }
    void print(){
      cout<<"height:"<<s_height<<endl;
      cout<<"width:"<<s_width<<endl;
    }
  private:
    int s_height;
    int s_width;
};

/* *
 *繼承頂層Size類
 * */
class Tv:public Size{
  public:
    Tv(){
      cout<<"Tv constructor"<<endl;
    }
    ~Tv(){
      cout<<"Tv destructor"<<endl;
    }
    void playVideo(){
      cout<<"play video"<<endl;
    }

  private:
};

/* *
 * 繼承底層Size類
 * */
class Phone:public Size{
  public:
    Phone(){
      cout<<"phone constructor"<<endl;
    }
    ~Phone(){
      cout<<"phone destructor"<<endl;
    }
    void ring(){
      cout<<"take ring"<<endl;
    }
    void receiverRing(){
      cout<<"receive ring"<<endl;
    }
    void sendMessage(){
      cout<<"sendMessage"<<endl;
    }
  private:
};

class SmartPhone:public Tv,public Phone{
  public:
    SmartPhone(){
      cout<<"SmartPhone constructor"<<endl;
    }
    ~SmartPhone(){
      cout<<"SmartPhone destructor"<<endl;
    }

    void printAll(){
      cout<<"height:"<<Size::getHeight()<<endl;
      cout<<"width:"<<Size::getWidth()<<endl;
    }
  private:
};






int main(int argc ,char *argv[]){
  SmartPhone smartPhone;
  cout<<"Tv size:"<<sizeof(Tv)<<endl;
  cout<<"Phone size"<<sizeof(Phone)<<endl;
  //如果不去進行虛繼承的話,這個時候會產生函式的二義性,也就是在編譯的時候找不到具體執行的方法
  smartPhone.setWidth(1280);
  smartPhone.setHeight(720);
  smartPhone.print();
  //子類虛繼承父類之後,可以避免這種二義性
  smartPhone.ring();
  smartPhone.receiverRing();
  smartPhone.sendMessage();
  smartPhone.playVideo();
  smartPhone.printAll();

  return 0;
}

上述程式碼在編譯的時候是通不過的,因為沒有virtual進行修飾的話,這個時候會產生函式的二義性,也就是函式在執行的時候,並不是去動態載入的.

報錯資訊如下所示:

MultiInheritance.cpp: In member function ‘void SmartPhone::printAll()’:
MultiInheritance.cpp:108:40: error: ‘Size’ is an ambiguous base of ‘SmartPhone’
       cout<<"height:"<<Size::getHeight()<<endl;
                                        ^
MultiInheritance.cpp:109:38: error: ‘Size’ is an ambiguous base of ‘SmartPhone’
       cout<<"width:"<<Size::getWidth()<<endl;
                                      ^
MultiInheritance.cpp: In function ‘int main(int, char**)’:
MultiInheritance.cpp:124:14: error: request for member ‘setWidth’ is ambiguous
   smartPhone.setWidth(1280);
              ^
MultiInheritance.cpp:34:10: note: candidates are: void Size::setWidth(int)
     void setWidth(int width){
          ^
MultiInheritance.cpp:34:10: note:                 void Size::setWidth(int)
MultiInheritance.cpp:125:14: error: request for member ‘setHeight’ is ambiguous
   smartPhone.setHeight(720);
              ^
MultiInheritance.cpp:38:9: note: candidates are: void Size::setHeight(int)
    void setHeight(int height){
         ^
MultiInheritance.cpp:38:9: note:                 void Size::setHeight(int)
MultiInheritance.cpp:126:14: error: request for member ‘print’ is ambiguous
   smartPhone.print();
              ^
MultiInheritance.cpp:48:10: note: candidates are: void Size::print()
     void print(){
          ^
MultiInheritance.cpp:48:10: note:                 void Size::print()

下面上一個完整程式碼:

/*
 * ===========================================================================
 *
 *       Filename:  MultiInheritance.cpp
 *    Description:
 *    智慧手機是繼承了智慧裝置,現在的手機都能看電視,也繼承了功能手機的打電話,發簡訊的功能
 *        Version:  1.0
 *        Created:  2017年06月20日 22時05分41秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace::std;

/* *
 *
 *建立一個頂層的Size類
 * */
class Size{
  public:
    Size(){
      cout<<"Size constructor"<<endl;
    }
    ~Size(){
      cout<<"size destructor"<<endl;
    }
    void setWidth(int width){
      s_width = width; 
    }

   void setHeight(int height){
     s_height = height;
   }

   int getWidth() const{
     return s_width;
   }
   int getHeight() const{
     return s_height;
   }
    void print(){
      cout<<"height:"<<s_height<<endl;
      cout<<"width:"<<s_width<<endl;
    }
  private:
    int s_height;
    int s_width;
};

/* *
 *繼承頂層Size類
 * */
class Tv:virtual public Size{
  public:
    Tv(){
      cout<<"Tv constructor"<<endl;
    }
    ~Tv(){
      cout<<"Tv destructor"<<endl;
    }
    void playVideo(){
      cout<<"play video"<<endl;
    }

  private:
};

/* *
 * 繼承底層Size類
 * */
class Phone:virtual public Size{
  public:
    Phone(){
      cout<<"phone constructor"<<endl;
    }
    ~Phone(){
      cout<<"phone destructor"<<endl;
    }
    void ring(){
      cout<<"take ring"<<endl;
    }
    void receiverRing(){
      cout<<"receive ring"<<endl;
    }
    void sendMessage(){
      cout<<"sendMessage"<<endl;
    }
  private:
};

class SmartPhone:public Tv,public Phone{
  public:
    SmartPhone(){
      cout<<"SmartPhone constructor"<<endl;
    }
    ~SmartPhone(){
      cout<<"SmartPhone destructor"<<endl;
    }

    void printAll(){
      cout<<"height:"<<Size::getHeight()<<endl;
      cout<<"width:"<<Size::getWidth()<<endl;
    }
  private:
};






int main(int argc ,char *argv[]){
  SmartPhone smartPhone;
  cout<<"Tv size:"<<sizeof(Tv)<<endl;
  cout<<"Phone size"<<sizeof(Phone)<<endl;
  //如果不去進行虛繼承的話,這個時候會產生函式的二義性,也就是在編譯的時候找不到具體執行的方法
  smartPhone.setWidth(1280);
  smartPhone.setHeight(720);
  smartPhone.print();
  //子類虛繼承父類之後,可以避免這種二義性
  smartPhone.ring();
  smartPhone.receiverRing();
  smartPhone.sendMessage();
  smartPhone.playVideo();
  smartPhone.printAll();
  return 0;
}

執行結果如下所示:

Size constructor
Tv constructor
phone constructor
SmartPhone constructor
Tv size:16
Phone size16
height:720
width:1280
take ring
receive ring
sendMessage
play video
height:720
width:1280
SmartPhone destructor
phone destructor
Tv destructor
size destructor

但是進行虛擬繼承過後,子類在繼承父親的變數的時候.會同時也會生成一個虛繼承函式表,這個時候會使類所佔用的記憶體資源增加.
有興趣的可以試試列印一下.在不進行虛擬程序的時候,Tv和Phone的size是8,但是進行虛擬繼承之後,增加了一個指標的空間,8個位元組,所以虛繼承後顯示出來的是16個位元組

相關推薦

新手學C/C++:函式是否必須為虛擬函式?什麼情況下才應該定義函式虛擬函式

多型是面向物件的一個基本屬性,包括靜態多型(編譯階段)和動態多型(執行階段),靜態多型主要是指函式引數不同產生的多型性,是在編譯階段可以識別的一種多型機制,而執行時多型則主要用於基類指標指向派生類物件時,可以通過基類指標直接呼叫派生類的物件函式,當然這種多型是通過虛擬函式實現的。 虛擬函式的目的

C++學習:虛擬函式,虛擬函式(virtual),繼承,函式

C++學習:虛擬函式,虛繼承,純虛擬函式(virtual)虛解構函式 虛擬函式 純虛擬函式 虛解構函式 虛繼承 簡介 在java這種高階語言中,有abstract和interface這兩個關鍵字.代表的是抽象類和介面,但是在C++這門語言中

C++學習之深入理解虛擬函式--虛擬函式表解析

前言 C++中的虛擬函式的作用主要是實現了多型的機制。關於多型,簡而言之就是用父類型別的指標指向其子類的例項,然後通過父類的指標呼叫實際子類的成員函式。這種技術可以讓父類的指標有“多種形態”,這是一種泛型技術。所謂泛型技術,說白了就是試圖使用不變的程式碼來實現可變的演算

C++ 第六章(多型性和虛擬函式)下篇(函式虛擬函式

一,虛解構函式 如果用new運算子建立了臨時物件,若基類中有解構函式並且定義了一個指向該基類的指標變數。在程式用帶指標引數的delete運算子撤銷物件時,會發生:系統只執行基類的解構函式,而不執行派生類的解構函式。 #include<iostream> using

[收集]c++抽象類、虛擬函式以及巧用函式實現介面類

在Java、C#中有關鍵詞abstract指明抽象函式、抽象類,但是在C++中沒有這個關鍵詞,很顯然,在C++也會需要只需要在基類宣告某函式的情況,而不需要寫具體的實現,那C++中是如何實現這一功能的,答案是純虛擬函式。 含有純虛擬函式的類是抽象類,不能生成物件,只能派生。

C++學習之多型篇(虛擬函式函式的實現原理--虛擬函式表)

通過下面的程式碼來說明: #include <iostream> #include <stdlib.h> #include <string> using namespace std; /**  *  定義動物類:Animal  *  成員

C++學習第13篇-虛擬函式

1. 衍生類中基類的指標和引用 上一篇中,已經介紹了類的繼承;這篇中,將介紹繼承中另一個重要和實用的方面-虛擬函式。 在討論虛擬函式之前,我們應明確為什麼需要虛擬函式。之前我們知道,衍生類包含了基類的一部分和本身的一部分。 輸出: 因為一個Derive的物

c++中的 虛擬函式 虛擬函式 基類

虛擬函式 ,純虛擬函式, 虛基類,它們都和virtual有關,這三個帶有虛字的定義容易使人混淆,下面先從作用上來解釋這三個定義的區別: 1.虛擬函式是用於多型中virtual修飾父類函式,確保父類指標呼叫子類物件時,執行子類函式的。 2.純虛擬函式是用來定義介面的

C/C++基類的函式為什麼必須定義為虛擬函式

C/C++基類的解構函式為什麼必須定義為虛擬函式?   為什麼基類的解構函式是虛擬函式? 在實現多型時,當用基類操作派生類,在析構時防止只析構基類而不析構派生類的狀況發生。 (1)第一種情況:沒有多型,建立派生類物件,基類的解構函式不是虛擬函式 #include<

函式虛擬函式

虛解構函式 通過基類的指標刪除派生類物件時,通常情況只調用基類的解構函式 但是,刪除一個派生類物件時,應該先呼叫派生類的解構函式,然後呼叫基類的解構函式(構造時自頂向下,析構時自底向上) 這種情況會產生記憶體洩漏,最終導致系統應可用記憶體不足而崩潰 解決辦法 把基類的解構函式宣告為virtual,此

C++中為什麼要將函式定義成虛擬函式

      派生類的成員由兩部分組成,一部分是從基類那裡繼承而來,一部分是自己定義的。那麼在例項化物件的時候,首先利用基類建構函式去初始化從基類繼承而來的成員,再用派生類建構函式初始化自己定義的部分。 同時,不止建構函式派生類只負責自己的那部分,解構函式也是,所以派生

c++為什麼函式要寫成虛擬函式

//base_derive.cpp #include <iostream> #include <memory> using namespace std; class Base{ public: Base(){cout << "Base" <&l

C++ 虛擬函式

轉自:http://see.xidian.edu.cn/cpp/biancheng/view/247.html 當派生類的物件從記憶體中撤銷時一般先呼叫派生類的解構函式,然後再呼叫基類的解構函式。但是,如果用new運算子建立了臨時物件,若基類中有解構函式,並且定義了一個指向該基類的指標變數。在程

C++ 基類的函式為什麼需要定義為虛擬函式

主要是因為當通過父類的指標或引用指向子類的物件時,刪除子類時父類的解構函式不能被呼叫造成記憶體洩露。 1.當基類的引用指向基類的物件 #include<iostream> #include<cstring> #include<cstdlib> using

c++物件模型-虛擬函式

看《inside the c++ object model》第二章,彙總一下一些知識點 ( 轉自:http://www.cppblog.com/aaxron/archive/2010/12/23/137293.html) 作為通常原則,如果一個類定義了虛擬函式,那麼它的解構函式就應該定義成虛

函式可以呼叫虛擬函式嗎?

1.子類解構函式可以呼叫純虛擬函式.正常實現多型. 2.基類不可以呼叫純虛擬函式,編譯出錯 #include <iostream> class A { public: A(){ std::cout << "A" << std::endl;

c++虛擬函式表與函式

由於本人才疏學淺,本文難免存在遺漏之處,歡迎大家留言指正,本人將感激不盡。 C++虛擬函式表與虛解構函式 1.靜態聯編和動態聯編 聯編:將原始碼中的函式呼叫解釋為要執行函式程式碼。 靜態聯編:編譯時能確定唯一函式。 在C中,每個函式名都能確定唯一的函式程式碼。

從零開始學C++之虛擬函式與多型(一):虛擬函式表指標、函式、object slicing與虛擬函式C++物件模型圖

#include <iostream>using namespace std;class CObject {public:     virtual void Serialize()     {         cout << "CObject::Serialize ..." <&

C++ 建構函式函式中能否呼叫虛擬函式

牛客網 ------------------- ------------------- ------------------- 設計模式 ------------------- -------------------

C++語言特性:建構函式,函式,虛擬函式,行內函數,靜態成員函式,過載,覆蓋,隱藏

C++中class預設屬性為private, struct預設屬性為public 建構函式:        1.初始化物件成員;        2.建立類物件;        由於虛擬函式是建立在物件的基礎上的,因此建構函式不能宣告為虛擬函式;虛擬函式是在執行的時候才識別,