以 virtual 繼承的類別轉形 |
尚未結案
|
jackyung
一般會員 發表:22 回覆:46 積分:13 註冊:2004-01-24 發送簡訊給我 |
因要以多重繼承的方式使用 Node 類別,所以繼承時加上 virtual,
但延伸類別物件的指標轉為基底類別時位址會調整,不像一般繼承方
式位址是一樣的,所以用強制轉形的方式從基底類別轉為延伸類別已
不可能了,編譯器也不允許這麼做,只能用 dynamic_cast 來轉 請問有其他辦法嗎?用 dynamic_cast 會很偒速度嗎?
dynamic_cast 這命令是否已被定為標準,可以在各平台上使用嗎?
class Node { int data; Node *Next; public: Node(int d) { data = d; Next = NULL; } virtual Node *GetNext(void); virtual void SetNext(Node *); GetData(void) { return data; } }; Node *Node::GetNext(void) { return Next; } void Node::SetNext(Node *N) { Next = N; } // 這裏使用 virtual 的方式繼承 class SNode: virtual public Node { int data2; public: SNode(int d):Node(d) { data2 = d * 2; } GetData2(void) { return data2; } }; // 測試 void __fastcall TForm1::Button1Click(TObject *Sender) { SNode *SN1 = new SNode(1); SNode *SN2 = new SNode(2); SN1->SetNext(SN2); // SN2 傳入後位址會改變 Node *N1 = SN1->GetNext(); int d1 = N1->GetData(); // int d2 = ((SNode*)N1)->GetData2(); // 無法這樣子轉形 int d2 = (dynamic_cast發表人 - jackyung 於 2004/10/03 04:35:48 |
justdo
高階會員 發表:2 回覆:359 積分:222 註冊:2004-08-17 發送簡訊給我 |
首先,除非你有多重繼承的需要,不然在繼承base class的時候不需要加上virtual的宣告,這只會拖累程式的速度 再來,從base class轉型成derived class本來就是不合理的事(不過VC6允許直接轉型...),除非你保證沒有人會直接使用base class,最簡單的方法就是讓base class只具有interface的宣告,這個請查閱相關書籍pure virtual function的部份,我這在裡只把GetNext改成純虛擬函式宣告,因此GetNext的實作就變成類別實作者的責任,要傳回derived class的指標,而不是讓client端取得base class指標再自己去轉換型態 用dynamic_cast應該不會對系統效能造成太大的影響,而且是C++的標準函式,就不用擔心移植的問題 程式結束之前,記得歸還你動態配置的記憶體 修改後的code如下
#include喔,對了,為了讓SNode能看到Node的Next變數,所以把Next改成protected宣告。 發表人 - justdo 於 2004/10/03 11:08:38 |
jackyung
一般會員 發表:22 回覆:46 積分:13 註冊:2004-01-24 發送簡訊給我 |
我在 BCB5 中追入 dynamic_cast 發現指令數量不少,因此不考慮使用 dynamic_cast 我模彷 Windows 的 COM 的 QueryInterface 的做法,效果還不錯,只是 ID 識別碼不知
要如何產生,我是用 BCB 提供的 __LINE__,只是若延伸類別在不同的檔案中,有可能會
重複,不知有沒有更好的辦法
class Node { int ID; // Node 識別碼 int data; Node *Next; public: Node(int d) { ID = __LINE__; // 這招只在同一份原始檔有效 data = d; Next = NULL; } virtual Node *GetNext(void); virtual void SetNext(Node *); // 依傳入的 Node 識別碼取得所要的介面 // 找不到會傳回 NULL virtual bool QueryInterface(int iid,void **Obj); GetData(void) { return data; } }; Node *Node::GetNext(void) { return Next; } void Node::SetNext(Node *N) { Next = N; } // 依傳入的 Node 識別碼取得所要的介面 // 找不到會傳回 NULL bool Node::QueryInterface(int iid,void **Obj) { if(iid == ID) { *Obj = this; return true; } else { *Obj = NULL; return false; } } // 這裏使用 virtual 的方式繼承 class SNode: virtual public Node { int ID; // Node 識別碼 int data2; public: virtual SNode *GetNext(void); // 依傳入的 Node 識別碼取得所要的介面 // 找不到會傳回 NULL virtual bool QueryInterface(int iid,void **Obj); SNode(int d):Node(d) { ID = __LINE__; // 這招只在同一份原始檔有效 data2 = d * 2; } GetData2(void) { return data2; } }; SNode *SNode::GetNext(void) { Node *N = Node::GetNext(); SNode *SN; if(N != NULL) { N->QueryInterface(ID,&(void *)SN); return SN; } else return NULL; } // 依傳入的 Node 識別碼取得所要的介面 // 找不到會傳回 NULL bool SNode::QueryInterface(int iid,void **Obj) { if(iid == ID) { *Obj = this; return true; } else // 往上一層類別繼續 { return Node::QueryInterface(iid,Obj); } } // 測試 void __fastcall TForm1::Button1Click(TObject *Sender) { SNode *SN1 = new SNode(1); SNode *SN2 = new SNode(2); SN1->SetNext(SN2); // SN2 傳入後位址會改變 Node *NP = SN1->GetNext(); SNode *SNP = SN1->GetNext(); int d1 = NP->GetData(); int d2 = SNP->GetData2(); delete SN1; delete SN2; } |
justdo
高階會員 發表:2 回覆:359 積分:222 註冊:2004-08-17 發送簡訊給我 |
|
jackyung
一般會員 發表:22 回覆:46 積分:13 註冊:2004-01-24 發送簡訊給我 |
引言: 就用類別的名字吧... 看你ID是要用char[] 型態,還是把名字編碼成數字當成ID使用都可以... 不過我覺得整個程式變得很複雜,而實際的效果卻等於下面一行程式: return (SNode*)(void*)(Next); 先把Next轉成void*再轉成SNode*...ID 用文字是可以,只是比較時又會拖慢速度,有沒有其他可自動產生數字的方法 不是只是轉成void*再轉成SNode*那麼簡單,而是利用呼叫 QueryInterface 時,會自動依所處的類別改變 this 的值,這是 OOP 的特性 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |