Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
368 views
in Technique[技术] by (71.8m points)

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

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 or socket; the frame contains a message type, and the message payload must be processed based on the message type.

Normally I write a polymorphic set of classes with accessor methods and a constructor which takes a reference to the message frame.

It occurs to me though that instead of constructing an accessor class based on a reference to the message frame, I could just derive the accessor classes directly from the message frame, and then reinterpret_cast from the message frame to the appropriate accessor class. This makes the code more concise and saves some bytes and processor cycles.

See the (extremely contrived and condensed) example below. Obviously for production code this would all need to be properly encapsulated, the cast made a member of the derived class, better separation of concerns imposed, and some validation added. This has all been removed for the sake of putting together a concise example.

#include <iostream>
#include <cstring>
#include <vector>

struct GenericMessage
{
  GenericMessage(const char* body):body_(body, body+strlen(body)){}
  std::vector<char> body_;  
};

struct MessageType1:public GenericMessage
{
    int GetFoo()const
    {
        return body_[2];
    }
    int GetBar()const
    {
        return body_[3];
    }    
};

int main() 
{
    GenericMessage myGenericMessage("1234");
    MessageType1* myMgessageType1 = reinterpret_cast<MessageType1*>(&myGenericMessage);
    std::cout << "Foo:" << myMgessageType1->GetFoo() << std::endl;
    std::cout << "Bar:" << myMgessageType1->GetBar() << std::endl;
    return 0;
}

I've never see this done anywhere. Is there any downside to casting from base to derived in this way, given that derived has no additional data members?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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;
    }
    

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...