1. 程式人生 > >合法的純虛解構函式?

合法的純虛解構函式?

有這樣一個問題:

1,C++一個類中可不可以擁有純虛的析構器?

2,純虛函式能具體定義?

先不直接思考這個問題,還是先想清楚一些基本的概念:

,一,虛擬函式

首先:強調一個概念
定義一個函式為虛擬函式,不代表函式為不被實現的函式。
定義他為虛擬函式是為了允許用基類的指標來呼叫子類的這個函式(多型性的基本手段)。
定義一個函式為純虛擬函式,才代表函式沒有被實現。

定義純虛擬函式是為了實現一個介面,起到一個規範的作用,規範繼承這個類的程式設計師必須實現這個函式。
1、簡介
假設我們有下面的類層次:

#include <iostream>  
using namespace std;

class A
{
public:
	virtual void foo()  //用virtual宣告的函式就叫虛擬函式
	{
		cout << "A::foo() is called" << endl;
	}
};
class B :public A
{
public:
	void foo()//覆蓋基類虛擬函式
	{
		cout << "B::foo() is called" << endl;
	}
};
int main(void)
{
	//等價方式1
	//A *a = new B();//典型的多型使用,向上轉型,基類的指標,卻指向派生類B,見下面的等價方式

	//等價方式2
	//A *a;//宣告指標a的型別
	//B *b = new B();//指標b的的型別為B,指向B類
	//a = b;//指標a指向B類

	//等價方式3
	A *a;
	B b;
	a = &b;
	a->foo();   // 在這裡,a雖然是A型別的指標,但是被呼叫的函式(foo)卻是B的,如果沒有虛關鍵字便是呼叫A的foo!  
	system("pause");
	return 0;
}

這個例子是虛擬函式的一個典型應用,通過這個例子,也許你就對虛擬函式有了一些概念。它虛就虛在所謂“推遲聯編”或者“動態聯編”上,一個類函式的呼叫並不是在編譯時刻被確定的,而是在具體執行時刻被確定的,判斷的依據是引用或者指標所繫結(指向)的物件的真實型別。由於編寫程式碼的時候並不能確定被呼叫的是基類的函式還是哪個派生類的函式,所以被成為“虛”函式。 虛擬函式只能藉助於指標或者引用來達到多型的效果(見上,每一種方式都用的是指標)。

1)如果基類的foo函式沒有virtual關鍵字,那麼由於派生類的物件的記憶體模型是基類的物件再加本身物件多增加的部分,在將派生類物件的地址賦給a時,c++編譯器將會把指標a進行型別轉換,他會認為儲存的就是基類物件的地址(實際上不管指標a指向的是派生類的物件還是自身的物件,在記憶體模型中都是先指向基類的物件記憶體),在執行foo函式當然呼叫基類的了。當我們在構造派生類的物件,首先呼叫基類的建構函式去構造基類的物件,然後才去呼叫派生類的建構函式,從而拼接出派生類物件的構造。

2)如果基類的foo函式有virtual關鍵字,那麼C++將採用遲繫結技術,即在編譯時不確定具體的呼叫函式,而根據在執行時的實際指向的物件型別來確定呼叫函式。

二,純虛擬函式

1、定義
純虛擬函式是在基類中宣告的虛擬函式,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法。純虛擬函式是一種特殊的虛擬函式,它的一般格式如下:
class <類名>
{
virtual <型別><函式名>(<引數表>)=0;
…
};

2、引入原因

     1)、為了方便使用多型特性,我們常常需要在基類中定義虛擬函式。
  2)、在很多情況下,基類本身生成物件是不合情理的。例如,動物作為一個基類可以派生出老虎、孔雀等子類,但動物本身生成物件明顯不合常理。
  為了解決上述問題,引入了純虛擬函式的概念,將函式定義為純虛擬函式(方法:virtual ReturnType Function()= 0;),則編譯器要求在派生類中必須予以重寫以實現多型性。同時含有純虛擬函式的類稱為抽象類,它不能生成物件。這樣就很好地解決了上述兩個問題。聲明瞭純虛擬函式的類是一個抽象類。所以,使用者不能建立類的例項,只能建立它的派生類的例項。
3)純虛擬函式最顯著的特徵是:它們必須在繼承類中重新宣告函式(不要後面的=0,否則該派生類也不能例項化),而且它們在抽象類中往往沒有定義。定義純虛擬函式的目的在於,使派生類僅僅只是繼承函式的介面。
4)純虛擬函式的意義,讓所有的類物件(主要是派生類物件)都可以執行純虛擬函式的動作,但類無法為純虛擬函式提供一個合理的預設實現。所以類純虛擬函式的宣告就是在告訴子類的設計者,“你必須提供一個純虛擬函式的實現,但我不知道你會怎樣實現它”。


三,抽象類

抽象類是一種特殊的類,它是為了抽象和設計的目的為建立的,它處於繼承層次結構的較上層。
(1)抽象類的定義:  稱帶有純虛擬函式的類為抽象類。
(2)抽象類的作用:
抽象類的主要作用是將有關的操作作為結果介面組織在一個繼承層次結構中,由它來為派生類提供一個公共的根,派生類將具體實現在其基類中作為介面的操作。所以派生類實際上刻畫了一組子類的操作介面的通用語義,這些語義也傳給子類,子類可以具體實現這些語義,也可以再將這些語義傳給自己的子類。
(3)使用抽象類時注意:

•   抽象類只能作為基類來使用,其純虛擬函式的實現由派生類給出。如果派生類中沒有重新定義純虛擬函式,而只是繼承基類的純虛擬函式,則這個派生類仍然還是一個抽象類。如果派生類中給出了基類純虛擬函式的實現,則該派生類就不再是抽象類了,它是一個可以建立物件的具體的類。
•   抽象類是不能定義物件的。

四,提出問題:

1,C++一個類中可不可以擁有純虛的析構器?

2,純虛函式能具體定義?

可以,的確有可能存在純虛析構器。實際上純虛析構器在標準C++中是合法的,並且有一點值得記住的是,如果一個類包含了純虛析構器那麼就必須為其提供函式體。那麼如果有函式體又何談純虛呢?我們知道在類的派生中析構器是反向的呼叫。這就意味著派生類的析構器將首相呼叫,然後基類析構器才被呼叫。如果純虛析構器的定義沒有提供那麼當物件銷燬時什麼函式體應該被呼叫呢?所以,編譯器和聯結器執行純存在了的虛析構器函式體。

來看一段程式:

#include <iostream>
class Base//基類
{
public:
    virtual ~Base()=0; // 純虛析構器,基類變成了抽象類
};
 
class Derived : public Base//從Base中派生
{
public:
    ~Derived()//派生類的析構器
    {
        std::cout << "~Derived() is executed";
    }
};
 
int main()
{
    Base *b=new Derived();
    delete b;//呼叫析構器,首先呼叫子類的析構器
    return 0;
}

聯結器將產生下面的錯誤:
test.cpp:(.text$_ZN7DerivedD1Ev[__ZN7DerivedD1Ev]+0x4c): 
undefined reference to `Base::~Base()' //未定義的基類析構器引用

現在如果我們提供純虛析構器具體的定義,這段程式便能夠編譯並且執行。

#include <iostream>
class Base
{
public:
    virtual ~Base()=0; // 基類的純虛析構器
};
Base::~Base()
{
    std::cout << "Pure virtual destructor is called";//純虛析構器的定義
}
 
class Derived : public Base
{
public:
    ~Derived()
    {
        std::cout << "~Derived() is executed\n";//
    }
};
 
int main()
{
    Base *b = new Derived();//指標b指向Base,b並不是Base的例項化物件
    delete b;//先呼叫子類析構器,再呼叫基類的,然而基類的是純虛析構器,一樣要為純虛虛構器定義
    return 0;
}

輸出為:
~Derived() is executed
Pure virtual destructor is called


必須記住的是當一個類有純虛擬函式時,這個類就變成了抽象類,不能生成物件,只能作為基類。

維基百科對此的解釋為(見最後一句):

純虛擬函式或純虛方法是一個需要被非抽象派生類執行的虛擬函式. 包含純虛方法的類被稱作抽象類; 抽象類不能被直接呼叫, 一個抽象基類的一個子類只有在所有的純虛擬函式在該類(或其父類)內給出實現時, 才能直接呼叫. 純虛方法通常只有宣告(簽名)而沒有定義(實現).
作為一個例子, 抽象基類"MathSymbol"可能提供一個純虛擬函式 doOperation(), 和派生類 "Plus" 和 "Minus" 提供doOperation() 的具體實現. 由於 "MathSymbol" 是一個抽象概念, 為其每個子類定義了同一的動作, 在 "MathSymbol" 類中執行 doOperation() 沒有任何意義. 類似的, 一個給定的 "MathSymbol" 子類如果沒有 doOperation() 的具體實現是不完全的.
雖然純虛方法通常在定義它的類中沒有實現, 在 C++ 語言中, 允許純虛擬函式在定義它的類中包含其實現, 這為派生類提供了備用或預設的行為.

參考資源:

【1】,部分文字轉載自,http://blog.csdn.net/hackbuteer1/article/details/7558868

【2】,維基百科:http://zh.wikipedia.org/wiki/虛擬函式_(程式語言)#.E6.8A.BD.E8.B1.A1.E7.B1.BB.E5.92.8C.E7.BA.AF.E8.99.9A.E5.87.BD.E6.95.B0

【3】,部落格園網友,哨兵,http://www.cnblogs.com/phenixyu/p/4249351.html

【4】,《C++Primer》第五版