2010-05-16 71 views
0

我觉得这个问题的答案很简单,但我真的无法找到它。所以这里有云:根据对象的动态类型调用一组重载函数

假设你有以下类:

class Base; 
class Child : public Base; 

class Displayer 
{ 
public: 
    Displayer(Base* element); 
    Displayer(Child* element); 
} 

另外,我有一个Base* object可能指向任何类Base的实例或类Child的一个实例。

现在我想创建基于元素上的Displayer通过object指出,但是,我要挑构造的正确版本。由于我现在有它,这将做到这一点(我是一个有点模糊我的C++在这里,但我认为这最清楚的方式)

object->createDisplayer(); 

virtual void Base::createDisplayer() 
{ 
    new Displayer(this); 
} 

virtual void Child::createDisplayer() 
{ 
    new Displayer(this); 
} 

这工作,然而,与此问题:

BaseChild是应用系统的一部分,而Displayer是GUI系统的一部分。我想独立于应用程序系统构建GUI系统,以便更换GUI。这意味着BaseChild不应该知道Displayer。但是,我不知道如何在不让Application类知道GUI的情况下实现这一点。

我错过了一些非常明显的东西,还是我尝试了一些不可能的事情?

编辑:我错过了原来问题中的一部分问题。这在GUI代码中发生得相当深,提供了这一个GUI独有的功能。这意味着我希望BaseChild类完全不知道有关该呼叫 - 而不仅仅是隐藏呼叫的内容

+0

我试图让标题更具表现力。我希望你同意。 – sbi 2010-05-16 21:18:36

+0

我很喜欢这个标题,谢谢 – Jasper 2010-05-16 21:34:35

回答

3

这似乎是双派遣的经典场景。避免双重调度的唯一方法是切换您应该避免的类型(if(typeid(*object) == typeid(base)) ...)。

你可以做的是让回拨机制通用的,以使应用程序不必知道GUI的:

class app_callback { 
    public: 
    // sprinkle const where appropriate... 
    virtual void call(base&) = 0; 
    virtual void call(derived&) = 0; 
}; 

class Base { 
    public: 
    virtual void call_me_back(app_callback& cb) {cb.call(*this);} 
}; 
class Child : public Base { 
    public: 
    virtual void call_me_back(app_callback& cb) {cb.call(*this);} 
}; 

然后,您可以使用此机器是这样的:

class display_callback : public app_callback { 
    public: 
    // sprinkle const where appropriate... 
    virtual void call(base& obj) { displayer = new Displayer(obj); } 
    virtual void call(derived& obj) { displayer = new Displayer(obj); } 

    Displayer* displayer; 
}; 

Displayer* create_displayer(Base& obj) 
{ 
    display_callback dcb; 
    obj.call_me_back(dcb); 
    return dcb.displayer; 
} 

你必须有层次结构中的每个类一个app_callback::call()功能,你将不得不增加每次一到每个回调你添加一个类层次结构。
因为在你的情况下,只需调用base&是可能的,也是如此,当你忘记重载这些函数在回调类中的编译器将不会引发错误。它只会打电话给一个采取base&。那很糟。

如果需要,可以使用CRTP将每个类的相同代码call_me_back()移动到私有继承类模板中。但是,如果你只有六门课,它并不能真正增加这么多的清晰度,它需要读者理解CRTP。

+0

虽然你的代码是我的问题的解决方案,但它不是我正在寻找的......问题在于'object-> createDisplayer();'在GUI代码中很深,提供了自己的功能这个图形用户界面 - 这意味着我不希望游戏有任何可能的回调... – Jasper 2010-05-16 21:53:05

+0

@Jasper:我不认为你可以避免这种情况。至少不是不采取切换式,这更糟糕。无论如何,我的建议会在您的层次结构的类中创建一个通用钩子,任何代码都可以钩入该层级。 'display_callback'可能在GUI代码中很深,游戏不需要知道它。它只知道通用的'app_callback'类型。 – sbi 2010-05-16 22:08:11

+1

这种模式通常被称为“访问者”,我相信它就是你真正想要的:下层只理解通用的“访问者”(回答中的回调),并且你可以创建具体的图形显示创建者访问者在GUI层中,仅使用较低层进行分派。 – 2010-05-16 23:34:39

0

让应用程序在系统代码上设置工厂接口。这是一个黑客入侵的方式来做到这一点。显然,将这​​些更改应用于您自己的偏好和编码标准。在某些地方,我正在内联类声明中的函数 - 只是为了简洁起见。

// PLATFORM CODE 
// platformcode.h - BEGIN 
class IDisplayer; 
class IDisplayFactory 
{ 
    virtual IDisplayer* CreateDisplayer(Base* pBase) = 0; 
    virtual IDisplayer* CreateDisplayer(Child* pBase) = 0; 
}; 

namespace SystemDisplayerFactory 
{ 
    static IDisplayFactory* s_pFactory; 
    SetFactory(IDisplayFactory* pFactory) 
    { 
     s_pFactory = pFactory; 
    } 

    IDisplayFactory* GetFactory() 
    { 
     return s_pFactory; 
    } 
}; 
// platformcode.h - end 

// Base.cpp和Child.cpp落实 “CreateDisplayer” 方法如下

void Base::CreateDisplayer() 
{ 
    IDisplayer* pDisplayer = SystemDisplayerFactory::GetFactory()->CreateDisplayer(this); 
} 


void Child::CreateDisplayer() 
{ 
    IDisplayer* pDisplayer = SystemDisplayerFactory::GetFactory()->CreateDisplayer(this); 
} 

//在应用程序代码,这样做:

#include "platformcode.h" 

    class CDiplayerFactory : public IDisplayerFactory 
    { 
     IDisplayer* CreateDisplayer(Base* pBase) 
     { 
      return new Displayer(pBase); 
     } 

     IDisplayer* CreateDisplayer(Child* pChild) 
     { 
      return new Displayer(pChild); 
     } 
    } 

然后某处在应用程序初始化(主或WinMain)初期,请说明以下内容:

CDisplayerFactory* pFactory = new CDisplayerFactory(); 
SystemDisplayFactory::SetFactory(pFactory); 

这将使您的平台代码不必知道“显示器”是什么的混乱细节,您可以稍后实现模拟版本的IDisplayer,以独立于渲染系统测试Base和Child。

此外,IDisplayer(方法未显示)成为平台代码公开的接口声明。你的“Displayer”实现是一个继承自IDisplayer的类(在你的应用代码中)。

+0

这确实解决了我提出的问题,但有一点问题:这一切都发生在GUI代码深处,提供了其他GUI可能根本不提供的功能...... – Jasper 2010-05-16 21:54:42

+0

我可能已经将您的平台对象与您的应用程序对象混淆了。但没关系。关键在于接口提供了不应该彼此了解的组件之间的逻辑抽象。对于其他“根本不可能提供”的gui,您仍然可能不得不提取抽象以使其与代码的其余部分兼容。识别与另一个gui的共同点,并使用通用接口编写抽象层。 – selbie 2010-05-16 23:11:38

相关问题