c ++设计:避免使用现有的类层次结构迭代类型

时间:2013-11-19 14:24:41

标签: c++ polymorphism

请考虑以下(简化)类层次结构和处理函数:

struct msgBase
{
    virtual int msgType() const=0;
};

struct msgType1:public msgBase
{
    virtual int msgType() const{return 1;}
};

struct msgType2:public msgBase
{
    virtual int msgType() const {return 2;}
};

void process(const msgType1& mt1)
{
    // processing for message type 1
}

void process(const msgType2& mt2)
{
    // processing for message type 2
}

void process(const msgBase& mbase)
{
    switch(mbase.msgType())
    {
    case 1:
        process(static_cast<const msgType1&>(mbase));
        break;
    case 2:
        process(static_cast<const msgType2&>(mbase));
        break;
    }
}

在集成设计中,msgBase将被赋予一个虚拟的“进程”方法,以避免需要迭代类型。

如果不可能或不希望修改任何类,是否有任何替代方法可以迭代这些类型?

我已经尝试过装饰器/工厂模式,其中类的并行层次结构封装了给定的类,并实现了必要的虚函数,但这导致了大量的样板,并且工厂函数仍然需要迭代类型

我可以用一系列dyamic_cast替换switch语句,但仍然存在相同的缺点。

3 个答案:

答案 0 :(得分:2)

按照西蒙的要求,这就是CRTP的意思:

typedef <class Derived>
struct msgBase
{
    virtual void process(){
        // redirect the call to the derived class's process()
        static_cast<Derived*>(this) -> process();
};

struct msgType1:public msgBase<msgType1>
{
    void process(){
        // process as per type-1
    }
};

struct msgType2:public msgBase<msgType1>
{
    void process(){
        // process as per type-2
    }
};

这里发生了什么?考虑这种情况:

msgBase* msg = new msgType1();
msg->process();

通常(没有CRTP)这只会调用msgBase::process()。但现在,msgBase“知道”使用模板msgType1,因此在编译时将其重定向到msgType1::process

答案 1 :(得分:1)

这样的事情可行:

这些类用于自动进行转换:

struct dispatcher_base {
    virtual void process(const msgBase&) = 0;
};

template <class T>
struct dispatcher_impl : dispatcher_base {
    void process(const msgBase& b) override {
        ::process(static_cast<const T&>(b));
    }
};

我们会将它们存储在map

auto g_table = std::map<int, std::unique_ptr<dispatcher_base>>{};

但现在你必须在某个地方初始化这个表:

template <class T>
void register_msg() {
    g_table[T{}.msgType()].reset(new dispatcher_impl<T>{});
}
...
register_msg<msgType1>();
register_msg<msgType2>();

您可以向assert添加register_msg,以确保msgType是唯一的。

您的process功能如下所示:

void process(const msgBase& b) {
    assert(g_table.find(b.msgType()) != g_table.end());
    g_table[b.msgType()]->process(b);
}

当然,您可以将assert替换为任何其他逻辑。

答案 2 :(得分:1)

如果无法修改类,则可以使用装饰器来获取多态类型。

struct DecorBase {
    DecorBase(msgBase& b) : b_(b) {}
    virtual ~DecorBase() {}
    virtual void process() = 0;
    msgBase& b_;
};

struct DecorType1 : public DecorBase {
    DecorType1(msgType1& t1) : DecorBase(t1) {}
    void process() override {
        std::cout << "Processing Type 1" << std::endl;
    }
};

struct DecorType2 : public DecorBase {
    DecorType2(msgType2& t2) : DecorBase(t2) {}
    void process() override {
        std::cout << "Processing Type 2" << std::endl;
    }
};

并像这样使用它:

msgType1 t1;
msgType2 t2;
DecorType1 dt1(t1); // Wrap objects in respective decorator.
DecorType2 dt2(t2);

DecorBase& base = dt2;
base.process();     // Uses polymorphism to call function in derived type.

这将要求你为每个派生类型编写一个装饰器,但至少你不必在函数调用期间迭代所有类型。