2012-08-31 108 views
1

嗨,大家好!將派生類對象傳遞給模板類的構造函數

我的問題如下:

我有,其存儲指向其它類的方法的模板類(當然模板類不知道最初類型的類將被傳遞給它的)。 我做模板類的構造函數取一個指向類過去了,另一種說法是方法的地址在課堂上通過,我想存儲,如下所示:

template <typename MyClass> 
    TemplateClass 
    { 
    typedef void (MyClass::*MethodPointer)(); 
    MyClass* theClass; 
    MethodPointer methodPointer; 
public: 
    TemplateClass(MyClass* cl, MethodPointer func) : theClass(cl), methodPointer(func){} 

    void Excute() 
    { 
     return (theClass->*methodPointer)(); 
    } 
    }; 

然後,我創建了一個基類,並從中派生出一個子類。

class BaseClass 
    { 
     // other details are omitted... 
     virtual void DoSomething(); 
    }; 

    class DerivedClass : public BaseClass 
    { 
     // other details are omitted... 
     void DoSomething(); 
    }; 

    // the definitions of the methods is omitted because it had no relevance to the issue 

然後,我提出的模板類的一個typedef服用基類作爲參數如:

typedef TemplateClass<BaseClass> TypedTemplateClass; 

然後,當我嘗試將派生類的指針傳遞給構造TypedTemplateClass,編譯器說,它不能轉換從派生類的基類的參數,如下所示:

DerivedClass* myDerivedObject = new DerivedClass(); 
    TypedTemplateClass* myTemplateObject = new TypedTemplateClass(myDerivedObject, &DerivedClass::DoSomething); 

但是如果我通過了基類本身的對象,一切效果很好!如下:

BaseClass* baseObject = new BaseClass(); 
    TypedTemplateClass* myTemplateObject2 = new TypedTemplateClass(baseObject, &BaseClass::DoSomething); 

那麼,有人可以啓發我解決這個問題嗎? 我知道問題出在該類型的模板類期待一個基類對象,但我需要通過派生類對象 - 因爲我打算讓不同的派生類,並能夠多態地通過他們的方法模板類。 我也知道,我可能只是忽略TypedTemplateClass定義,只是創建模板類對象,每一個不同的派生類類型。但是,上述提議是我的意圖。

我正在使用visual studio IDE

在此先感謝您的關心和幫助。

+0

它是正常的在你的'DerivedClass'中'DoSomething'不是虛擬的? –

+1

你是第一個兩個片段無效的C++。很難說你真的在做什麼...... – jrok

+0

@ J.N。不一定,它可以是虛擬的。 – user1638717

回答

4

的問題是,你不能將一個指針到方法,從指針到法-的衍生型,以指針到法 - 的基地型,因爲指針到方法與對象類型是contravariant

考慮:

Base instance; 
void (Base::*pmethod)(); 
pmethod = &Base::doSomething; 
(instance.*pmethod)(); // OK 

如果你被允許寫

pmethod = &Derived::doSomethingElse; 

那麼你可以使用pmethodBase類型的instance調用Derived::doSomethingElse

根據Liskov's substitution principle,派生對象(引用)是 - 基礎對象(引用),因爲您可以對基礎執行的任何操作都可以對派生進行,但是可以對派生的指針進行指針操作,不是指向基礎方法的指針;事實上,這是另一種方式(指向基礎方法的指針 - 派生的指針),這就是爲什麼我們說指針到方法是逆變的原因:

void (Derived::*pmethod)() = &Base::doSomething; 

在你的情況下,最好的辦法可能是編寫一個模板化的構造函數,並使用類型擦除來隱藏Derived方法指針的類型;在以下

template<typename T> 
TemplateClass(T *instance, void (T::*pmethod)()); 

兩個T S IN的構造方法的類型可以相互抵消,得到函數簽名void()。你可以用std::function<void()>成員做到這一點:

Base *instance; 
std::function<void()> type_erased_method; 

template<typename T> 
TemplateClass(T *instance, void (T::*pmethod)()): 
    instance(instance), 
    type_erased_method(std::bind(pmethod, instance)) 
{ 
} 
+0

感謝您的好解釋。 – user1638717

+0

感謝您的解釋。我嘗試了你的方式,現在它編譯成功。但是,在運行時,當應用程序到達了將地址存儲在模板類實例內的方法的執行階段時,它會收到訪問衝突錯誤並在那裏停止!對此有任何想法。 – user1638717

+0

錯誤是:在####。exe中0xcdcdcdcd未處理異常:0xC0000005:訪問衝突。 (其中####是文件名) – user1638717

0

這到底意味着:TemplateClass(MyClass* class, &MyClass::SomeMethod);這不是一個有效的C++代碼。你爲什麼不堅持使用C++標準庫?如果您需要存儲可調用函數,請使用從lambda構建的std::function。編譯器和庫將照顧所有必要的轉換...

+0

對不起,這是一個輸入錯誤。這是我在其他地方調用構造函數的方式,而不是聲明或定義。我已經在我的問題中糾正了這個錯誤。謝謝。 – user1638717

0

那麼,我所做的項目是一個試驗,使一個機制來處理C + +類似的事件 - 或多或少對C#方式的事件和事件處理器。 因此,在研究了一些文本並找出合適的方法後,我編寫了兩個模板類: 一個是存儲成員方法的指針並將其稱爲'EventHandler'。 另一個是存儲事件處理程序的映射並根據需要調用它們;這是'Event'類。然後我寫了兩個'正常'類:一個是事件激發類,另一個是響應或聽課。 事件和事件處理程序類的最初版本如下:

#include <functional> 

namespace eventhandling 
{ 
#ifndef __BASIC_EVENT_HANDLER__ 
#define __BASIC_EVENT_HANDLER__ 

//////////////////////////////////////////////////////////////////////////// 
// the root event handler class 
//////////////////////////////////////////////////////////////////////////// 
template <typename empty = int, empty = 0> 
class BaseEventHandler 
{ 
public: 
    virtual void Excute() = 0; 
}; 


/////////////////////////////////////////////////////////////////////////// 
// the basic derived event handler class; the class which will wrap the 
// methods of other classes which want to respond to specific event(s).. 
/////////////////////////////////////////////////////////////////////////// 
template <typename ResponderType> 
class EventHandler : public BaseEventHandler < > 
{ 
    typedef void (ResponderType::*MethodPointer()); 

    ResponderType* responder; 
    MethodPointer methodPointer; 

public: 
    EventHandler(ResponderType* resp, MethodPointer func) : responder(resp), methodPointer(func) 
    {} 

    void Excute() 
    { 
     return methodPointer(); 
    } 
}; 

#endif 
} 

#include "BasicEventHandler.h" 
#include <map> 

namespace eventhandling 
{ 
#ifndef __BASIC_EVENT__ 
#define __BASIC_EVENT__ 

//////////////////////////////////////////////////////////////////////////////////////////////// 
// the event class which will receive these event handlers, stores them in a map object, 
// and call them squentially when invoked from within the event firing method... 
//////////////////////////////////////////////////////////////////////////////////////////////// 

// the template takes no parameters, so I added an empty parameter, just because 
//it cannot ignore the parameter list, otherwise it will be considered template specialization 
template <typename empty = int, empty = 0> 
class BasicEvent 
{ 
    //store the eventhandlers in a map so that I can track them from outside the class by id 
    typedef std::map<int, BaseEventHandler<empty>* > Responders; 
    Responders responders; 
    int respondersCount; 

public: 
    BasicEvent() : respondersCount(0) 
    {} 

    // classical add method templatized so that it accepts any object 
    template <typename Responder> 
    int Add(Responder* sender, void (Responder::*memberFunc)()) 
    { 
     responders[respondersCount] = (new EventHandler<Responder>(sender, memberFunc)); 
     respondersCount++; 
     return respondersCount - 1; 
    } 

    // simple method to clean up the map memory after done with the eventhandlers 
    void Remove(int responderID) 
    { 
     Responders::iterator it = responders.find(responderID); 
     if (it == responders.end()) 
      return; 
     delete it->second; 
     responders.erase(it); 
    } 

    // method which invokes all the eventhandlers alltogether without control from the outside 
    void Invoke() 
    { 
     Responders::iterator it = responders.begin(); 
     for (; it != responders.end(); ++it) 
     { 
      it->second->Excute(); 
     } 
    } 

    // method which invokes only the eventhandler whose id has been passed to it 
    void Invoke(int id) 
    { 
     Responders::iterator it = responders.find(id); 
     if (it != responders.end()) 
      it->second->Excute(); 
    } 

    // overloaded operator+= to replace the functionality of method Add() 
    template <typename Responder> 
    void operator+=(EventHandler<Responder>* eventhandler) 
    { 
     responders[respondersCount] = eventhandler; 
     respondersCount++; 
    } 

    // overloaded operator -= to replace the functionality of method Remove() 
    void operator-=(int id) 
    { 
     Responders::iterator it = responders.find(id); 
     if (it == responders.end()) 
      return; 
     delete it->second; 
     responders.erase(it); 
    } 

    //simple method which gives the size of the map object 
    int Size() 
    { 
     return respondersCount; 
    } 
}; 
#endif 
} 

然後,我想在創建一個新的事件處理程序的對象,這顯然是擺脫了明確的「< ... ..>」模板語法什麼是C#的情況下,所以,我把它簡單地像下面這樣:

typedef EventHandler<MyClass> SomeEventFired_EventHandler; 

這樣,當我需要創建這個模板的新對象,我只需要編寫:

MyClass* myObject = new MyClass(); 
    MyEventFiringClass* firingObject = new MyEventFiringClass(); 
    firingObject->OnFired += new SomeEventFired_EventHandler(myObject, &MyClass::SomeMethod); 

當然,後面的完整代碼示例將使其更加清晰! 這裏是我的問題,我希望能夠傳遞MyClass派生類的對象。問題在於上面顯示的EventHandler模​​板不接受這樣的派生對象,因爲它預期只有基類的對象,並且編譯器抱怨說它不能從派生類轉換到基類。 當他向我展示使EventHandler類的構造函數模板化的正確方法時,ecatmur提供了非常寶貴的幫助。 這樣,當我使用MyClass類型定義SomeEventFired_EventHandler作爲基類時,只要對象來自派生自MyClass的類,我就能夠將任何對象及其方法傳遞給其構造函數。這是我實現EventHandler多態特性的最終目標。 我想要這個功能,因爲如果你在C#中檢查EventHandlers,你可以看到System :: EventHandler是多態的,它接受來自類Object的不同的對象,我基本上是從類Object派生的。 所以,下面是一個完整的例子,以及基於ecatmur解決方案的糾正過的EventHandler類,供大家閱讀,我希望你會發現它有一些好處。 最終,您可以從BaseEventHandler類派生,以便派生的EventHandler可以存儲具有不同返回類型和不同參數參數的方法,因爲此處顯示的基本方法接受返回void並採用void的方法(我相信您可以通過只是改變的std ::功能<的聲明>,使其接受其他類型的方法,例如,

std::function<int(int)> 

,依此類推)。

事件類是與上述相同的...

#include <functional> 

namespace eventhandling 
{ 
#ifndef __BASIC_EVENT_HANDLER__ 
#define __BASIC_EVENT_HANDLER__ 

//////////////////////////////////////////////////////////////////////////// 
// the root event handler class 
//////////////////////////////////////////////////////////////////////////// 
template <typename empty = int, empty = 0> 
class BaseEventHandler 
{ 
public: 
    virtual void Excute() = 0; 
}; 


/////////////////////////////////////////////////////////////////////////// 
// the basic derived event handler class; the class which will wrap the 
// methods of other classes which want to respond to specific event(s).. 
/////////////////////////////////////////////////////////////////////////// 
template <typename ResponderType> 
class EventHandler : public BaseEventHandler < > 
{ 
    std::function<void()> type_erased_method; 
    ResponderType* responder; 

public: 

    template<typename T> 
    EventHandler(T* resp, void (T::*MethodPointer)()) : responder(resp), type_erased_method(std::bind(MethodPointer, resp)) 
    {} 

    void Excute() 
    { 
     return type_erased_method(); 
    } 
}; 

#endif 
} 

事件擊發類的頭文件...

#include <iostream> 
#include <string> 
#include "BasicEvent.h" 

namespace eventhandling 
{ 
#ifndef __FONT_SIMULATOR__ 
#define __FONT_SIMULATOR__ 

typedef BasicEvent<> FontEvent; 
typedef std::string s; 

class FontSimulator 
{ 

private: 
    s fontName; 
    s fontSize; 
    s fontStyle; 
public: 
    FontSimulator(); 
    FontSimulator(s name, s size, s style); 
    ~FontSimulator(); 

    FontEvent OnDraw; 

    void DrawText(); 

    // the setting methods 
    void SetFontName(s n) {fontName = n;} 
    void SetFontSize(s si) {fontSize = si;} 
    void SetFontStyle(s st) {fontStyle = st;} 

    // the getting methods 
    s GetFontName() {return fontName;} 
    s GetFontSize() {return fontSize;} 
    s GetFontStyle() {return fontStyle;} 
}; 
#endif 
} 

其源文件,的.cpp

#include "FontSimulator.h" 

using namespace eventhandling; 

FontSimulator::FontSimulator() : fontName("Default Name"), fontSize ("Default Size"), fontStyle("Default Style") 
{ 
} 

FontSimulator::FontSimulator(s fName, s fSize, s fStyle) : fontName(fName), fontSize(fSize), fontStyle(fStyle) 
{ 
} 

FontSimulator::~FontSimulator() 
{ 
delete this; 
} 

void FontSimulator::DrawText() 
{ 
std::cout << "Initialization of font done!" << std::endl << std::endl; 
std::cout << fontName << std::endl; 
std::cout << fontSize << std::endl; 
std::cout << fontStyle << std::endl << std::endl; 

for (int i = 0; i < OnDraw.Size(); ++i) 
{ 
    OnDraw.Invoke(i); 
    std::cout << "the #" << i + 1 << " responder method called!" << std::endl << std::endl; 
    std::cout << fontName << std::endl; 
    std::cout << fontSize << std::endl; 
    std::cout << fontStyle << std::endl << std::endl; 
} 
for (int j = 0; j < OnDraw.Size(); j++) 
{ 
    //OnDraw.Remove(j); 
    OnDraw -= j; 
} 

     std::cout << "The finishing font work after all the event handler are called!" << std::endl <<std::endl; 
    } 

抽象基處理字體類事件的類...

#include "BasicEventHandler.h" 

namespace eventhandling 
{ 
#ifndef __IFONT_CLIENT__ 
#define __IFONT_CLIENT__ 

class IFontClient 
{ 
public: 
    IFontClient(){}; 
    ~IFontClient(){delete this;} 
    virtual void SetupFont() = 0; 
}; 

typedef EventHandler<IFontClient> FontEventHandler; 

#endif 
} 

從IFontClient ...頭文件第一派生類

#include "BasicEventHandler.h" 
#include "BasicEvent.h" 
#include "FontSimulator.h" 
#include "IFontClient.h" 

namespace eventhandling 
{ 
#ifndef __CONTROL_SIMULATOR__ 
#define __CONTROL_SIMULATOR__ 

class ControlSimulator : public IFontClient 
{ 
protected: 
    std::string caption; 
    FontSimulator* font; 

public: 
    ControlSimulator(); 
    ControlSimulator(std::string theCaption, FontSimulator* theFont); 
    ~ControlSimulator(); 

    virtual void Draw(); 
    virtual void SetupFont(); 

    void SetCaption(std::string c) {caption = c;} 
    std::string GetCaption() {return caption;} 
}; 

#endif 
} 

其源文件的.cpp

#include "ControlSimulator.h" 

namespace eventhandling 
{ 
ControlSimulator::ControlSimulator() : caption("Default Caption"), font(new FontSimulator()) 
{ 
} 

ControlSimulator::ControlSimulator(std::string c, FontSimulator* f) : caption(c), font(f) 
{ 
} 

ControlSimulator::~ControlSimulator() 
{ 
    delete this; 
} 

void ControlSimulator::Draw() 
{ 
    std::cout << "Drawing " << caption << " is done!" << std::endl << std::endl; 
} 

void ControlSimulator::SetupFont() 
{ 
    std::string costumProperty = caption; 
    font->SetFontName(costumProperty.append(", Costumized Font Name")); 

    costumProperty = caption; 
    font->SetFontSize(costumProperty.append(", Costumized Font Size")); 

    costumProperty = caption; 
    font->SetFontStyle(costumProperty.append(", Costumized Font Style")); 
} 
} 

主要進入測試應用

#include "ControlSimulator.h" 

using namespace eventhandling; 

int main(int argc, char** argv) 
{ 
char c; 

FontSimulator* font = new FontSimulator(); 
ControlSimulator* control1 = new ControlSimulator("Control one", font); 
ControlSimulator* control2 = new ControlSimulator("Control two", font); 

control1->Draw(); 
control2->Draw(); 

font->OnDraw += new FontEventHandler(control1, &ControlSimulator::SetupFont); 
font->OnDraw += new FontEventHandler(control2, &ControlSimulator::SetupFont); 

font->DrawText(); 

std::cout << "Enter any character to exit!" << std::endl; 
std::cin >> c; 

return 0; 
} 
相關問題