根据对象的动态类型从一组重载调用函数

时间:2010-05-16 20:53:42

标签: c++ inheritance

我觉得这个问题的答案非常简单,但我找不到它。所以这里:

假设您有以下类:

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系统的一部分。我想独立于Application系统构建GUI系统,因此很容易替换GUI。这意味着BaseChild不应该知道Displayer。但是,我不知道如何在不让Application类了解GUI的情况下实现这一目标。

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

编辑:我在原始问题中错过了部分问题。这一切都在GUI代码中发生得相当深刻,提供了这个GUI独有的功能。这意味着我希望BaseChild类根本不知道该调用 - 不只是隐藏它们到调用的内容

2 个答案:

答案 0 :(得分: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。

答案 1 :(得分: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的类(在您的应用程序代码中)。