線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:1019
推到 Plurk!
推到 Facebook!

以 virtual 繼承的類別轉形

尚未結案
jackyung
一般會員


發表:22
回覆:46
積分:13
註冊:2004-01-24

發送簡訊給我
#1 引用回覆 回覆 發表時間:2004-10-03 04:32:42 IP:218.171.xxx.xxx 未訂閱
因要以多重繼承的方式使用 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(N1))->GetData2(); // 只能這樣轉
}    
發表人 - jackyung 於 2004/10/03 04:35:48
justdo
高階會員


發表:2
回覆:359
積分:222
註冊:2004-08-17

發送簡訊給我
#2 引用回覆 回覆 發表時間:2004-10-03 11:02:22 IP:221.169.xxx.xxx 未訂閱
首先,除非你有多重繼承的需要,不然在繼承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 
#pragma hdrstop
#pragma argsused    class Node
{
protected:
  int data;
  Node *Next;
public:
  Node(int d)
  {
    data = d;
    Next = NULL;
  }
  virtual Node *GetNext(void)=0;
  virtual void SetNext(Node *);
  int GetData(void)
  {
    return data;
  }
};    void Node::SetNext(Node *N)
{
  Next = N;
}    class SNode:public Node
{
private:
  int data2;
public:      SNode(int d):Node(d)
  {
    data2 = d * 2;
  }      SNode* GetNext()
  {
    return dynamic_cast(Next);
  }      int GetData2()
  {
    return data2;
  }
};    int main(int argc, char* argv[])
{
  SNode *SN1 = new SNode(1);
  SNode *SN2 = new SNode(2);
  SN1->SetNext(SN2);
  SNode *N1 = SN1->GetNext();
  int d1 = N1->GetData();
  int d2 = N1->GetData2();
  delete SN1;
  delete SN2;
  return 0;
}    
喔,對了,為了讓SNode能看到Node的Next變數,所以把Next改成protected宣告。 發表人 - justdo 於 2004/10/03 11:08:38
jackyung
一般會員


發表:22
回覆:46
積分:13
註冊:2004-01-24

發送簡訊給我
#3 引用回覆 回覆 發表時間:2004-10-03 18:09:42 IP:218.171.xxx.xxx 未訂閱
我在 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

發送簡訊給我
#4 引用回覆 回覆 發表時間:2004-10-03 23:06:05 IP:221.169.xxx.xxx 未訂閱
就用類別的名字吧... 看你ID是要用char[] 型態,還是把名字編碼成數字當成ID使用都可以... 不過我覺得整個程式變得很複雜,而實際的效果卻等於下面一行程式: return (SNode*)(void*)(Next); 先把Next轉成void*再轉成SNode*...
jackyung
一般會員


發表:22
回覆:46
積分:13
註冊:2004-01-24

發送簡訊給我
#5 引用回覆 回覆 發表時間:2004-10-04 01:27:58 IP:218.171.xxx.xxx 未訂閱
引言: 就用類別的名字吧... 看你ID是要用char[] 型態,還是把名字編碼成數字當成ID使用都可以... 不過我覺得整個程式變得很複雜,而實際的效果卻等於下面一行程式: return (SNode*)(void*)(Next); 先把Next轉成void*再轉成SNode*...
ID 用文字是可以,只是比較時又會拖慢速度,有沒有其他可自動產生數字的方法 不是只是轉成void*再轉成SNode*那麼簡單,而是利用呼叫 QueryInterface 時,會自動依所處的類別改變 this 的值,這是 OOP 的特性
系統時間:2024-06-26 13:30:30
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!