如何在没有向下转换的情况下调用派生类的成员函数

时间:2012-10-17 10:52:33

标签: c++ inheritance override abstract-class virtual-functions

我有一个Message类和Processor类。每个处理器可以即时接收一条或多条消息。由于每条消息都有一些不同的属性,我要将该消息向下转发到concrect消息类,以实际处理该消息。
  因为没有。消息类和进程类,我不想使用dynamic_cast 我试图使用以下代码,但这给出了编译时错误。 此外,我可以灵活地将处理器指针与消息(如果需要)相连,但不是相反。

class Message  
{  
    public:  
    virtual const Message* const getMessage() const = 0;
};

class MA : public Message  
{  
    public:  
    const MA* const getMessage() const {return this;}
    void printMA() const{std::cout<<"I am MA"<<std::endl;}
};

class MB : public Message  
{  
    public:  
    const MB* const getMessage() const {return this;}
    void printMB() const{std::cout<<"I am MB"<<std::endl;}
};

class Processor  
{  
public:  
    virtual void process(const Message* m) = 0;

};

class PA : public Processor  
{  
    public:  
    void process(const Message* m) {processM(m->getMessage());}

    void processM(const MA*  m) {m->printMA();}
    void processM(const MB*  m) {m->printMB();}
};

int main()  
{  
    Message* m1 = new MA();  
    Message* m2 = new MB();  

    Processor* p1 = new PA();  
    p1->process(m1);
    p1->process(m2);
    return 0;
}

5 个答案:

答案 0 :(得分:2)

我最后使用'双重派遣'来解决这个问题。现在,唯一的事情是我需要在MessageProcessor'类中添加一个函数,每当我添加一个新的消息类型。但我认为这很好。

class MessageProcessor
{
    public:
        virtual void process(const MA*) const{std::cout<<"unhandled:MA"<<std::endl;}
        virtual void process(const MB*) const{std::cout<<"unhandled:MB"<<std::endl;}
        virtual void process(const MC*) const{std::cout<<"unhandled:MC"<<std::endl;}
};

class Message
{
    public:
    virtual void process(const MessageProcessor*) const = 0;
};

class MA : public Message
{
    public:
    void printMA() const{std::cout<<"I am MA"<<std::endl;}
    virtual void process(const MessageProcessor* p) const {p->process(this);}
};

class MB : public Message
{
    public:
    void printMB() const{std::cout<<"I am MB"<<std::endl;}
    virtual void process(const MessageProcessor* p) const {p->process(this);}
};

class MC : public Message
{
    public:
    void printMC() const{std::cout<<"I am MC"<<std::endl;}
    virtual void process(const MessageProcessor* p) const {p->process(this);}
};

class Processor : public MessageProcessor
{
    public:
    void processM(const Message* m){m->process(this);}

};

class PA : public Processor
{
    public:
    void process(const MA*  m) const {m->printMA();}
    void process(const MB*  m) const {m->printMB();}
};

class PB : public Processor
{
    public:
    void process(const MA*  m) const {m->printMA();}
    void process(const MC*  m) const {m->printMC();}
};

int main()
{
    const Message* m1 = new MA();
    const Message* m2 = new MB();
    const Message* m3 = new MC();

    Processor* p1 = new PA();
    p1->processM(m1);
    p1->processM(m2);
    p1->processM(m3);

    Processor* p2 = new PB();
    p2->processM(m1);
    p2->processM(m2);
    p2->processM(m3);

    return 0;
}

答案 1 :(得分:1)

最简单的做法是取消getMessage()方法,并在print()中制作Message纯虚拟广告,并在MAMB中覆盖此方法。此外,您可以在process()中将Process设为纯虚拟方法,并在PA中覆盖此方法。请参阅以下代码:

#include <iostream>

class Message  
{  
    public:  
    const std::string _id;

    Message(std::string id):_id(id) {}

    virtual void print() const = 0;
    virtual void other_fun() const = 0;
};

class MA : public Message  
{ 
    private: double d_; 
    public:  
    MA():Message("MA"), d_(0.0) {}

    virtual void print() const
    {
        std::cout<<"I am MA"<<std::endl;
        std::cout << "I also have a double" << std::endl; 
    }

    virtual void other_fun() const { std::cout << "I am MA specific" << std::endl; }

    void do_hoops () const { std::cout << "Hoop!"<<std::endl;}
};

class MB : public Message  
{  
    private: int i_;
    public:  
    MB():Message("MB"), i_(0) {}

    virtual void print() const
    {
        std::cout<<"I am MB"<<std::endl;
        std::cout << "I also have an int"<<std::endl;
    }

    virtual void other_fun() const { std::cout << "I am MB specific" << std::endl; }

    void do_twist() const { std::cout << "Twist!"<<std::endl; }
};

class Processor  
{  
public:  
    const std::string _id;
    Processor(std::string id) : _id(id){}

    virtual void process(const Message* m) = 0;

};

class PA : public Processor  
{  
    public:  
    PA():Processor("PA") {}

    virtual void process(const Message* m) 
    {
        m->print();
        m->other_fun();
    }
};

int main()  
{  
    Message* m1 = new MA();  
    Message* m2 = new MB();  

    // generic handling of message
    Processor* p1 = new PA();  
    p1->process(m1);
    p1->process(m2);

    // message specific stuff
    dynamic_cast<MA*>(m1)->do_hoops();
    dynamic_cast<MB*>(m2)->do_twist();
    return 0;
}

Ideone上的输出。

不需要强制转换,虚拟函数将在运行时通过动态分派(虚拟表查找等)选择。 MessageProcess是抽象基类(“interfaces”)和MAMBPA是实现这些接口的具体类。理想情况下,您还会将std::string状态从Message接口中分解出来,但这仍然是一个练习。

如果您要调用特定于派生类的函数,并且如果您在运行时知道您实际上正在调用此类,则需要进行强制转换。这是通过dynamic_cast到您的基类指针当前指向的特定派生类来完成的。

答案 2 :(得分:1)

您问题的最常见解决方案可能是Visitor pattern

答案 3 :(得分:1)

你有一个设计缺陷。 Processor::process的签名建议它需要Message,然后它不应该通过尝试访问不是Message的公共接口的内容来违背此承诺。

您可以使Process继承自用户提供的策略的模板类(主机)。这里的政策是具体的Message类。像这样:

#include <iostream>

struct MA
{
    void print ()
    {  
        std::cout << "MA: I'm the interface" << std::endl;
    }

    void printMA ()
    {  
        std::cout << "MA: I'm special" << std::endl;
    }
};

struct MB
{
    void print ()
    {  
        std::cout << "MB: I'm the interface" << std::endl;
    }

    void printMB ()
    {  
        std::cout << "MB: I'm special" << std::endl;
    }
};

template <typename M>
struct Process :
    public M
{
    void process()
    {  
        M::print();
    }
};

int main ()
{
    Process<MA> p1;
    Process<MB> p2;

    p1.print();     // MA: I'm the interface
    p1.printMA();   // MA: I'm special

    p2.print();     // MB: I'm the interface
    p2.printMB();   // MB: I'm special
}

策略具有定义其接口的print方法。他们还有一些特殊的方法,如printMAprintMB。主机类(此处为Process)充当用户对策略的接口。它可以使用策略类中的接口方法。用户可以通过主机类调用特殊策略方法。

答案 4 :(得分:1)

你遇到了C ++的限制。你真正想要的是多态性处理方法的参数,而不仅仅是调用参数的方法。它通常被称为double dispatch。不幸的是,虽然有一些类型的解决方法,但我还没有看到任何完美的解决方案。维基百科的文章显示了普遍接受的解决方法(使用访问者模式)。