1. 程式人生 > 程式設計 >詳解C++純虛擬函式與抽象類

詳解C++純虛擬函式與抽象類

1.虛擬函式

1.1虛擬函式簡介

虛擬函式可以毫不誇張的說是C++最重要的特性之一,我們先來看一看虛擬函式的概念。

在基類的定義中,定義虛擬函式的一般形式為:

virtual 函式返回值型別 虛擬函式名(形參表)
{
 函式體
}

為什麼說虛擬函式是C++最重要的特性之一呢,因為虛擬函式承載著C++中動態聯編的作用,也即多型,可以讓程式在執行時選擇合適的成員函式。虛擬函式必須是類的非靜態成員函式(且非建構函式),其訪問許可權是public。那麼:
(1)為什麼類的靜態成員函式不能為虛擬函式?
如果定義為虛擬函式,那麼它就是動態繫結的,也就是在派生類中可以被覆蓋的,這與靜態成員函式的定義(在記憶體中只有一份拷貝,通過類名或物件引用訪問靜態成員)本身就是相矛盾的。

(2)為什麼建構函式不能為虛擬函式?
因為如果建構函式為虛擬函式的話,它將在執行期間被構造,而執行期則需要物件已經建立,建構函式所完成的工作就是為了建立合適的物件,因此在沒有構建好的物件上不可能執行多型(虛擬函式的目的就在於實現多型性)的工作。在繼承體系中,構造的順序就是從基類到派生類,其目的就在於確保物件能夠成功地構建。建構函式同時承擔著虛擬函式表的建立,如果它本身都是虛擬函式的話,又如何確保虛擬函式表的成功構建呢?

1.2虛解構函式

在類的繼承中,基類的解構函式一般都是虛擬函式。當基類中有虛擬函式的時候,解構函式也要定義為虛解構函式。如果不定義虛解構函式,當刪除一個指向派生類物件的指標時,會呼叫基類的解構函式,派生類的解構函式未被呼叫,造成記憶體洩露。

虛解構函式工作的方式是:最底層的派生類的解構函式最先被呼叫,然後各個基類的解構函式被呼叫。這樣,當刪除指向派生類的指標時,就會首先呼叫派生類的解構函式,不會有記憶體洩露的問題了。

一般情況下,如果類中沒有虛擬函式,就不用去宣告虛解構函式。當且僅當類裡包含至少一個虛擬函式的時候才去宣告虛解構函式。只有當一個類被用來作為基類的時候,才有必要將解構函式寫成虛擬函式。

1.3虛擬函式的實現——虛擬函式表

虛擬函式是通過一張虛擬函式表來實現的,簡稱V-Table。類的虛擬函式表是一塊連續的記憶體,每個記憶體單元中記錄一個JMP指令的地址。編譯器會為每個有虛擬函式的類建立一個虛擬函式表,該虛擬函式表將被該類的所有物件共享,類的每個虛擬函式成員佔據虛擬函式表中的一行。

在這個表中,存放的是一個類的虛擬函式的地址。這張表解決了繼承、覆蓋的問題,保證使用指向子類物件實體的基類指標或引用,能夠訪問到物件實際的虛擬函式。在有虛擬函式類的例項中,分配了指向這個表的指標的記憶體,所以,當用父類的指標來操作一個子類物件實體的時候,這張虛擬函式表就指明瞭實際所應該被呼叫的虛擬函式。

2.純虛擬函式與抽象類

既然有了虛擬函式,那為什麼還需要有純虛擬函式呢?在Java程式語言中有介面的定義,在C++中雖然沒有介面關鍵字,但是純虛擬函式就完成了介面的功能。而且有時在編寫基類的時候,發生瞭如下情況:
(1)功能不應由基類去完成;
(2)還沒想好應該如何寫基類的這個函式;
(3)有的時候基類本身不應被例項化。

這時就可以用到純虛函數了。下面我們通過一個例子比較虛擬函式和純虛擬函式的區別:

class Base 
{
public:
  //這是一個虛擬函式
  virtual void vir_func()
  {
   cout << "This is a virtual function of Base" << endl;
  }
  //這是一個純虛擬函式
  virtual void pure_vir_func() = 0;
};

由上可見,純虛擬函式在類中沒有定義函式體,並加上了“= 0”。而含有至少一個純虛擬函式的類被稱為抽象類。定義純虛擬函式和抽象類的目的在於,僅僅只定義派生類繼承的介面,而暫時無法提供一個合理的預設實現。所以純虛擬函式的宣告就是在告訴子類的設計者,“你必須實現這個函式,但我不知道你會怎樣實現它”。

值得特別注意的是,由於抽象類至少有一個函式沒有實現,所以抽象類無法被例項化,否則編譯器會報錯。

下面看一下純虛擬函式與抽象類的例項。本實驗在GNU C++環境下進行。

#include <iostream>
using namespace std;

class Base
{
public:
  //這是一個虛擬函式
  virtual void vir_func()
  {
    cout << "This is a virtual function of Base" << endl;
  }

  //這是一個純虛擬函式
  virtual void pure_vir_func() =0;
};

class Derive : Base
{
public:
  void vir_func()
  {
    cout << "This is a virtual function of Derive" << endl;
  }

  void pure_vir_func()
  {
    cout << "This is a pure virtual function of Derive" << endl;
  }
};

int main()
{
  // Base b; //企圖例項化抽象類,編譯器報錯
  // b.vir_func();

  Derive d;
  d.vir_func();
  d.pure_vir_func();

  return 0;
}

輸出:

This is a virtual function of Derive
This is a pure virtual function of Derive

派生類Derive實現了基類Base類的虛擬函式和純虛擬函式,同時注意到,企圖去例項化抽象類,編譯器會報錯。

一般而言,純虛擬函式沒有函式體,但是也可以給出純虛擬函式的函式體,所以下面這樣的結構是可以通過編譯的:

class Base
{
public:
  //這是一個虛擬函式
  virtual void vir_func()
  {
   cout << "This is a virtual function of Base" << endl;
  }

  //這是一個純虛擬函式
  virtual void pure_vir_func() =0
  {
   cout << "This is a pure virtual function of Base" << endl;
  }
};

但這樣做並沒有什麼意義,因為抽象類並不能例項化,不能呼叫該方法。

以上就是詳解C++純虛擬函式與抽象類的詳細內容,更多關於C++純虛擬函式與抽象類的資料請關注我們其它相關文章!