本文介绍了避免使用派生类的dynamic_cast(Cast Derived类)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是C ++的新手,来到一个点,在那里我生成一个类的开销。我有一个QTcpSocket并从它读取消息并创建对象,例如MessageJoin,MessagePart,MessageUserData等。我发送这些对象到我的客户端,并显示它们(+做一些UI更新)。



现在这里是我的问题。我测试了一些设计技术,但是所有的都不是那么好:




  • 在信号/槽连接中传递消息对象的每个参数

  • 为每个消息类型(messageJoinReceived,messageNoticeReceived等)创建一个方法。

  • 创建一个方法,并使用dynamic_cast转换每个类并测试



为了更好的理解,我添加了我的dynamic_cast版本。正如所说,代码看起来丑陋和不可用。 我的问题


  • 是否有另一种方式(例如设计模式)来解决这样的问题?或者在类中添加一个方法并返回类型或类似的东西。

  • 我阅读了访问者模式。这个模式只是用于Getter / Setter方法中的动态对象类型?



  • 几个侧音符




    • 可以使用RTTI

    • 速度不是一件大事。清除和可理解的代码更重要

    • 我使用Qt并有可能使用qobject_cast和signal / slots



    这是我的代码():

      //默认类 - 包含完整的消息(未修改)
    类消息
    {
    public:
    QString virtual getRawMessage (){return dataRawMessage; }
    protected:
    QString dataRawMessage;
    };

    //加入类 - cointains加入的用户和通道的名称
    class MessageJoin:public Message
    {
    public:
    MessageJoin QString& rawmessage,const QString& channel,const QString& user)
    {
    dataRawMessage = rawmessage;
    dataChannel = channel;
    dataUser = user;
    }

    QString getChannel(){return dataChannel; }
    QString getUser(){return dataUser; }

    private:
    QString dataChannel;
    QString dataUser;
    };

    //通知类 - 包含通知消息
    class MessageNotice:public Message
    {
    public:
    MessageNotice(const QString& rawmessage,const QString& text)
    {
    dataRawMessage = rawmessage;
    dataText = text;
    }

    QString getText(){return dataText;}

    private:
    QString dataText;
    };

    //客户端代码 - 打印消息并更新UI
    void Client :: messageReceived(消息*消息)
    {
    if(message)
    {
    MessageJoin * messagejoin;
    MessagePart * messagepart;
    MessageNotice * messagenotice;
    if((messagejoin = dynamic_cast< MessageJoin *>(message))!= 0)
    {
    qDebug()< messagejoin-> getUser()<< 连接<< messagejoin-> getChannel();
    //更新UI:添加用户
    }
    else if((messagenotice = dynamic_cast< MessageNotice *>(message))!= 0)
    {
    qDebug ()<< messagenotice-> getText();
    //更新UI:显示消息
    }
    else
    {
    qDebug()< 无法转换消息对象;
    }
    删除消息; //消息在库中分配并且不再使用
    }
    }


    解决方案

    这看起来与非常相似, AFAIK没有办法避免施法,如果你要添加新的消息和新的方法来处理它们。然而,不是那么难以使更多的眼睛悦目包装必要的运行时东西。只需使用 typeid 创建一个从邮件类型到相应处理程序的地图。

     

    #include< functional>
    #include< typeindex>
    #include< typeinfo>
    #include< unordered_map>

    typedef std :: function< void(Message *)> handler_t;

    typedef std :: unordered_map<
    std :: type_index,
    handler_t> handlers_map_t;

    template< class T,class HandlerType>
    handler_t make_handler(HandlerType handler)
    {
    return [=](Message * message){handler(static_cast< T *>(message)); };
    }

    template< class T,class HandlerType>
    void register_handler(
    handlers_map_t& handlers_map,
    HandlerType handler)
    {
    handlers_map [typeid(T)] = make_handler< T&
    }

    void handle(handlers_map_t const& handlers_map,Base * message)
    {
    handlers_map_t :: const_iterator i = handlers_map.find(typeid );
    if(i!= handlers_map.end())
    {
    (i-> second)
    }
    else
    {
    qDebug()<< 无法处理消息对象;
    }
    }

    然后注册特定消息类型的处理程序:

     

    handlers_map_t handlers_map;

    register_handler< MessageJoin>(
    handlers_map,
    [](MessageJoin * message)
    {
    qDebug()<< message-> getUser ()<join<<< message-> getChannel();
    //更新UI:添加用户
    }

    register_handler< MessageNotice>(
    handlers_map,
    [](MessageNotice * message)
    {
    qDebug()<< message-> getText ();
    //更新UI:显示消息
    });

    现在您可以处理邮件:

     

    //简单测试
    消息* messages [] =
    {
    new MessageJoin 。),
    new MessageNotice(...),
    new MessageNotice(...),
    new MessagePart(...),
    };

    for(auto m:messages)
    {
    handle(handlers_map,m);
    delete m;
    }

    当然你可能想做一些改进,到可重用的类,使用QT或boost信号/槽,这样你可以有多个处理程序的单个消息,但核心思想是一样的。


    I am new to C++ and came to a point, where I generate an overhead with classes. I have a QTcpSocket and read messages from it and create objects, for example MessageJoin, MessagePart, MessageUserData etc. I send these objects to my client and display them (+ do some UI updating).

    Now here comes my problem. I tested a few design techniques but all of them are not that nice:

    • Pass each parameter of a message object in a signal/slot connection to the client - small overhead but not that good-looking
    • Create a method for each Message-Type (messageJoinReceived, messageNoticeReceived etc.)
    • Create one method and use dynamic_cast to cast für each class and test it

    For a better understanding, I added my dynamic_cast version. As a said, the code looks ugly and unusable. My questions are:

    • Is there a better way to do it with (a) dynamic_cast
    • Is there another way (For example a design pattern) to solve such a problem ? maybe add a method in the classes and return the type or something like this
    • I read about the visitor pattern. This pattern is just for dynamic object types in Getter/Setter methods ?

    A few side notes

    • I can use RTTI
    • Speed isn't a big deal. Clean and understandable code is more important
    • I use Qt and have the possiblity to use qobject_cast and signal/slots

    Here is my code (Pastebin-Link):

    // Default class - contains the complete message (untouched)
    class Message
    {
    public:
        QString virtual getRawMessage() { return dataRawMessage; }
    protected:
        QString dataRawMessage;
    };
    
    // Join class - cointains the name of the joined user and the channel
    class MessageJoin : public Message
    {
    public:
        MessageJoin(const QString &rawmessage, const QString &channel, const QString &user)
        {
            dataRawMessage = rawmessage;
            dataChannel = channel;
            dataUser = user;
        }
    
        QString getChannel() { return dataChannel; }
        QString getUser(){ return dataUser; }
    
    private:
        QString dataChannel;
        QString dataUser;
    };
    
    // Notice class - contains a notification message
    class MessageNotice : public Message
    {
    public:
        MessageNotice(const QString &rawmessage, const QString &text)
        {
            dataRawMessage = rawmessage;
            dataText = text;
        }
    
        QString getText() { return dataText;}
    
    private:
        QString dataText;
    };
    
    // Client code - print message and update UI
    void Client::messageReceived(Message *message)
    {
        if(message)
        {
            MessageJoin *messagejoin;
            MessagePart *messagepart;
            MessageNotice *messagenotice;
            if((messagejoin = dynamic_cast<MessageJoin *>(message)) != 0)
            {
                qDebug() << messagejoin->getUser() << " joined " << messagejoin->getChannel();
                // Update UI: Add user
            }
            else if((messagenotice = dynamic_cast<MessageNotice *>(message)) != 0)
            {
                qDebug() << messagenotice->getText();
                // Update UI: Display message
            }
            else
            {
                qDebug() << "Cannot cast message object";
            }
            delete message; // Message was allocated in the library and is not used anymore
        }
    }
    
    解决方案

    This looks quite similar to the expression problem and AFAIK there is no way to avoid casts if you are going to add new messages and new ways to handle them. However it's not that hard to make more eye pleasing wrap for necessary run-time stuff. Just create a map from message type to corresponding handler using typeid.

    
    #include <functional>
    #include <typeindex>
    #include <typeinfo>
    #include <unordered_map>
    
    typedef std::function<void(Message *)> handler_t;
    
    typedef std::unordered_map<
        std::type_index,
        handler_t> handlers_map_t;
    
    template <class T, class HandlerType>
    handler_t make_handler(HandlerType handler)
    {
        return [=] (Message *message) { handler(static_cast<T *>(message)); };
    }
    
    template <class T, class HandlerType>
    void register_handler(
        handlers_map_t &handlers_map,
        HandlerType handler)
    {
        handlers_map[typeid(T)] = make_handler<T>(handler);
    }
    
    void handle(handlers_map_t const &handlers_map, Base *message)
    {
        handlers_map_t::const_iterator i = handlers_map.find(typeid(*message));
        if (i != handlers_map.end())
        {
            (i->second)(message);
        }
        else
        {
            qDebug() << "Cannot handle message object";
        }
    }
    
    

    Then register handlers for specific message types:

    
    handlers_map_t handlers_map;
    
    register_handler<MessageJoin>(
        handlers_map,
        [] (MessageJoin  *message)
        {
            qDebug() << message->getUser() << " joined " << message->getChannel();
            // Update UI: Add user
        });
    
    register_handler<MessageNotice>(
        handlers_map,
        [] (MessageNotice *message)
        {
            qDebug() << message->getText();
            // Update UI: Display message
        });
    
    

    And now you can handle messages:

    
    // simple test
    Message* messages[] =
    {
        new MessageJoin(...),
        new MessageNotice(...),
        new MessageNotice(...),
        new MessagePart(...),
    };
    
    for (auto m: messages)
    {
        handle(handlers_map, m);
        delete m;
    }
    
    

    Surely you might want to make some improvements like wrapping handlers stuff into reusable class, using QT or boost signals/slots so you can have multiple handlers for a single message, but the core idea is the same.

    这篇关于避免使用派生类的dynamic_cast(Cast Derived类)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

    07-23 06:43
    查看更多