2012-11-10 44 views
3

我正在創建一個基於回調的類。我需要讓客戶自由地調用他定義的回調。例如,我有一個Base類:有什麼方法可以知道C++中的派生類是否已經實現了非純虛函數?

class Base 
{ 
... 
public: 
    virtual void OnMsgReceived(const char *msg, char *&response); 
    virtual void OnMsgReceived(const char *msg, string &response); 
}; 

客戶MUST實施任何一個,但不是2.我如何知道哪一個他已經實施 這樣,當我處理回調,我稱之爲正確對象,真愛?

我知道我不能在對象的構造實例化過程中做到這一點,但是一旦對象被實例化,是否有任何方法可以檢查這些虛擬函數中的哪一個已經實現?謝謝。

+1

爲什麼不能客戶端同時實現虛擬' '功能? – iammilind

+2

只要我的2美分,我會避免使用此設計,並提供似乎是單個事件的兩個回調,即OnMsgReceived,只要相同的條件導致回調被執行。如果他們真的不同,那麼他們應該有不同的命名。 – Matthew

回答

0

在沒有純粹的C++技術的virtual技術中,在運行時無法知道函數是否由類實現。

將這兩個函數定義爲純虛擬函數並讓客戶端實現它們兩者。如果需要,您仍然可以爲Base設置純虛功能。

客戶端必須實現一個,但不是2.

假設兩個版本都真正需要的,我不覺得有必要具有上述要求的。

我怎麼知道他實施了哪一個,以便在我處理 回調時,我打電話是正確的?

將適當的對象分配給Base引用/指針後,您不必擔心這一點。編譯器爲你做這個骯髒的工作。

在旁註,你也可以探索CRTP技術。如果派生類必須實現了給定的方法來傳遞編譯。但是,這不會像virtual功能那樣靈活。

0

我想的東西添加到iammilind's response

如果你在你製作的類抽象類使用純虛函數和,作爲結果,你不能實例化它。

如果A是一個抽象類

class A 
{ 
    public: 
    // 
    virtual void foo() = 0; 
    // 
} 

你不能寫

A myA 

一個很好的資源有關,這是,像往常一樣,在Parashift C++ FAQ website

0

荒謬的做法:從一個到另一個在基類中調度。除非用戶已經至少其中之一覆蓋,這將導致一個計算器,很容易檢測

void Base::OnMsgCallback(const char *msg, char*& response) { 
    std::string resp; 
    OnMsgCallback(msg,resp); 
    response = new char[resp.size()+1](); 
    std::copy(resp.begin(), resp.end(), response); 
} 
void Base::OnMsgCallback(const char *msg, std::string& response) { 
    char *resp; 
    OnMsgCallback(msg,resp); 
    response = resp; 
    delete [] resp; 
} 

但是,這是一個可怕的黑客,正如你可能已經注意到了char*版本需要手工處理資源,這是容易出錯的。您是通過定義一個接口傳遞到std::string參考(或有std::string返回類型,從而簡化了合同好得多(是responseINOUT或只是參數?)和密碼(不在調用回調之前需要創建std::string

+0

它實際上很聰明,但不是一個解決方案:當你檢測到錯誤時,它已經太晚了,stackoverflow!如果用戶重寫這兩個函數,它也失控。 –

0

要找出哪個函數已被覆蓋並不容易 - 即使可以,它也是開發人員很難遵循的設計:客戶端必須實現其中一個,但不是2 - 開發人員可能會忘記它,並且他們可能不喜歡它!

我建議根據你設計的另一種方式:

你的框架

class Base0 
{ 
public: 
    // other basic members 

}; 

template<class T> 
class Base : public Base0 
{ 
    virtual void OnMsgReceived(const char *msg, T &response) = 0; 
}; 


typedef Base<char*> BaseChar; 

typedef Base<string&> BaseString; 



void run(Base0* pBase) 
{ 
    const char* msg = getmsg(); 
    BaseChar* p = dynamic_cast<BaseChar*>(pBase); 
    if(p != NULL) 
    { 
     char* response; // 
     p->OnMsgReceived(msg, response) 
     return; 
    } 

    BaseString* p2 = dynamic_cast<BaseString*>(pBase); 
    if(p2 != NULL) 
    { 
     string response; // 
     p2->OnMsgReceived(msg, response) 
     return; 
    } 

} 

客戶端代碼

class Derived: public BaseChar 
{ 
public: 
    virtual void OnMsgReceived(const char *msg, char* &response) 
    { 
     // do sth 
    }  
}; 

你的框架的用戶需要實施BaseChar,或BaseString,那麼一切正常。

+0

我認爲'template class Base:public Base'中有一個輸入錯誤,應該不是'template class Base:public Base0'? – kcm1700

+0

@ kcm1700哦,是的,謝謝! –

0

設計是壞的這麼多的理由,反正有辦法做你的方式:

class Base 
{ 
public: 
    virtual void OnMsgReceived(const char *msg, char *&response) 
    { 
     response = (char*)"OMG! nothing derived"; // or throw if you want 
    } 

    virtual void OnMsgReceived(const char *msg, string &response) 
    { 
     char *ptr_to_dest = 0; 
     OnMsgReceived(msg, ptr_to_dest); 
     response = ptr_to_dest; 
    } 
}; 

總是呼叫使用的一個,你並不需要檢查哪些一個他們做了。

這是他們實現取決於會發生什麼:

  • 只實現焦炭版本 - >它會被稱爲並轉換成字符串
  • 只實現版本 - >它會直接調用
  • 執行均爲個版本 - >僅將被稱爲
  • 實現什麼 - >(如果你扔或除外),你會得到默認郵件
相關問題