c ++设计:从基类转换为派生类,没有额外的数据成员 [英] c++ design: cast from base to derived class with no extra data members

查看:138
本文介绍了c ++设计:从基类转换为派生类,没有额外的数据成员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了很多处理消息协议的代码。通常,消息协议将具有通用消息帧,可以从串行端口或套接字反序列化;框架包含消息类型,并且必须根据消息类型处理消息有效负载。

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.

虽然不是基于对消息帧的引用构造一个访问器类,但我可以直接从消息帧中派生出访问者类,并且然后从消息帧reinterpret_cast到相应的访问器类。这使代码更简洁,并节省了一些字节和处理器周期。

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?

推荐答案

这是为什么我不会使用这种技术:

Here is why I would not use this technique:


  1. 这违反了标准,导致行为未定义。这种情况可能几乎一直在起作用,但你不能在将来排除问题。编辑器已经看到在优化中使用未定义的行为,这对于毫无防备的程序员来说是非常不利的。并且您无法预测何时以及在何种情况下会发生这种情况。

  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.

您无法保证您和队友都不会添加某些数据成员到派生类型。您的类层次结构将会增长,随着时间的推移会添加更多代码;在某些时候,对于您或其他程序员而言,向派生类型添加无辜数据成员(即使是暂时的,可能用于某些调试目的)也可能不会显而易见。

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.

有一些清晰合法的替代方案,例如使用基于引用的包装:

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


这篇关于c ++设计:从基类转换为派生类,没有额外的数据成员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆