c++ design: cast from base to derived class with no extra data members

前端 未结 2 1385
别跟我提以往
别跟我提以往 2020-12-11 03:52

I write quite a lot of code which processes message protocols. Quite often a message protocol will have a generic message frame which can be deserialised from a serial port

相关标签:
2条回答
  • 2020-12-11 03:59

    No you can't !

    It may work in your case but it's not advisable since (quick explanation) derived class may have more members or virtual functions, which would not be available from the base.

    The easiest solution is to keep your inheritance scheme (which is good) but use a factory to instantiate the correct message type. Example :

    struct GenericMessage* create_message(const char* body) {
       int msg_type = body[5]; // I don't know where type is coded, this is an example
       switch(msg_type) {
       case 1:
          return new MessageType1(body);
          break;
       // etc.
    

    You can then safely dynamic_cast it later.

    Note that you can put your factory anywhere, for example in the GenericMessage class itself, i.e.

    GenericMessage myGenericMessage("1234");
    MessageType1* myMgessageType1 = myGenericMessage.get_specialized_message();
    

    Alternatively, you can also build a specialized message from a base one, but it's the same at the end :

    GenericMessage myGenericMessage("1234");
    MessageType1* myMgessageType1 = new MessageType1( myGenericMessage );
    
    0 讨论(0)
  • 2020-12-11 04:12

    Here is why I would not use this technique:

    1. It is a violation of the Standard and causes the behavior to be undefined. It is probably true that this works nearly all the time, but you can't rule out problems in the future. Compilers have been seen to make use of undefined behavior in optimizations, much to the disadvantage of the unsuspecting programmer. And you can't predict when and under what circumstances this will happen.

    2. You can't guarantee that neither you nor a team mate will ever add some data members to the derived type. Your class hierarchy will grow and more code will be added over time; at some point it may not be obvious to you or another programmer that adding an innocent data member to the derived type (even temporarily, perhaps for some debugging purpose) can spell disaster.

    3. There are clean and legal alternatives, for example using wrappers based on references:

      #include <iostream>
      
      struct Elem
      { };
      
      struct ElemWrapper
      {
        Elem &elem_;
      
        ElemWrapper(Elem &elem) : elem_(elem)
        { }
      };
      
      struct ElemWrapper1 : ElemWrapper
      {
        using ElemWrapper::ElemWrapper;
      
        void foo()
        { std::cout << "foo1" << std::endl; }
      };
      
      struct ElemWrapper2 : ElemWrapper
      {
        using ElemWrapper::ElemWrapper;
      
        void foo()
        { std::cout << "foo2" << std::endl; }
      };
      
      int main()
      {
        Elem e;
      
        ElemWrapper1(e).foo();
      
        return 0;
      }
      
    0 讨论(0)
提交回复
热议问题