組件編寫中的屬性繼承注意事項

在C++中, 函數的覆蓋(override,注:大多數文章及書本中把override也稱爲重載,我認爲不妥,在此借用Delphi術語“覆蓋”與“重載(overload)”區分)是在基類中用virtual關鍵字來修飾,稱爲虛函數,如果派生類中對基類中的虛函數進行覆蓋,那麼使用指向派生類的父類指針調用此函數,其實運行的是派生類中的版本。

例如:

#include <iostream>
using namespace std;

class a {
public:
   virtual void Hello() {cout<<"Class a"<<endl;}
};
class b : public a {
public:
   void Hello() {cout<<"Class b"<<endl;}
};

int main(int argc, char* argv[])
{
   a *ia = new a;
   ia->Hello(); //輸出"Class a"
   a *ib = new b;
   ib->Hello(); //輸出"Class b"
   delete ia;
   delete ib;
   return 0;
}

而在CBuilder和Delphi中增加了__property關鍵字,稱爲屬性,它可以指定一個讀方法和寫方法或者直接進行變量存取,例如:

__property AnsiString Name = {read=FName, write=SetName}; //TComponent 的Name屬性聲明。

(根據CBuilder/Delphi約定,直接存取的變量名用字母F開頭,讀方法用Get+屬性名,寫方法用Set+屬性名)

如果你從TComponent派生一個新類TMyComp,重新定義了Name屬性,有這麼幾種可能碰到的情況:
1. 爲了處理設置Name時的行爲,你重新定義了寫方法SetName,而且內部也定義了一個叫FName的變量。

__property AnsiString Name = {read=GetName, write=SetName}

如果這樣做,將得不到覆蓋的效果,因爲第一種情況下,對Name的賦值將是對TMyComp內部的FName的賦值,而不是對基類TComponent中的FName賦值,補救的辦法是去除TMyComp的FName變量並且把基類的FName放於protected段使派生類可以直接訪問(這樣做常常會使人莫名其妙,因爲在派生類對FName賦值卻沒有發現FName的定義);2. 重新聲明瞭屬性並更改讀方法

而第二種情況下聲明瞭GetName讀方法,由於基類TComponent中並沒有聲明虛函數GetName,所以這兩種情況下如果這樣寫:

TComponent *MyComp = new TMyComp(NULL);
MyComp->Name = "Hello";
ShowMessage(MyComp->Name);

最後顯示的是空字符而不是你想要的"Hello"。所以,如果基類屬性的聲明中讀寫方法中有一個是直接變量賦值或者是非虛函數方法。那麼爲了達到覆蓋的效果,它的派生類中將不能重新對這個屬性進行覆蓋定義(但可以降低限制等級或者更改默認值或者設爲只讀),除非你不在乎它失去函數覆蓋這個重要的性能。

(一點小經驗,如果有不對的地方請發表你的意見)

發佈了87 篇原創文章 · 獲贊 11 · 訪問量 44萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章