在以前的雇主,我们写的二进制消息必须"通过电线"到其他计算机.每条消息都有一个标准标题,例如:
class Header { int type; int payloadLength; };
所有数据都是连续的(标题,紧接着是数据).我们希望得到有效载荷,因为我们有一个指向标题的指针.传统上,您可能会说:
char* Header::GetPayload() { return ((char*) &payloadLength) + sizeof(payloadLength); }
甚至:
char* Header::GetPayload() { return ((char*) this) + sizeof(Header); }
这看起来很冗长,所以我想出了:
char* Header::GetPayload() { return (char*) &this[1]; }
起初看起来相当令人不安,可能使用起来太奇怪 - 但非常紧凑.关于它是辉煌还是令人憎恶,有很多争论.
那么它是什么 - 犯罪编码,还是不错的解决方案?你曾经有过类似的权衡吗?
- 更新:
我们确实尝试过零大小的数组,但当时编译器发出了警告.我们最终采用了以下技术:消息来自Header.它在实践中很有效,但原则上你说的是一条消息IsA Header - 这看起来有点尴尬.
我会去打击编码犯罪.
两种方法都将生成完全相同的目标代码.第一个是明确的意图.第二个是非常混乱,唯一的优点是它可以节省几次击键.(只是学习freakin'类型).
另外,请注意NEITHER方法可以保证正常工作.sizeof()一个对象包括用于字对齐的填充,因此如果标题是:
class Header { int type; int payloadLength; char status; };
您描述的两种方法都将使有效负载从Header + 12开始,而最有可能它实际上从Header + 9开始.
就个人而言,我认为如果有犯罪,那就是要求有效载荷的标题.
但只要你这样做,'这+ 1'就像任何一样好.
理由:'&this [1]'是一个通用的代码片段,不需要你深入挖掘类定义来完全理解,并且当有人更改类的名称或内容时不需要修复.
顺便说一句,第一个例子是真正的反人类罪.将一个成员添加到类的末尾,它将失败.移动类中的成员,它将失败.如果编译器填充类,它将失败.
此外,如果您假设编译器的类/结构布局与您的数据包布局匹配,那么您应该了解所讨论的编译器是如何工作的.例如.在MSVC你可能想知道#pragma pack
.
PS:有多少人认为"这+ 1"或"&[1]"很难阅读或理解,这有点可怕.
您依赖编译器以特定方式布置类.我会将消息定义为结构(我定义布局),并有一个封装消息并为其提供接口的类.清除代码=良好的代码."可爱"代码=糟糕(难以维护)的代码.
struct Header { int type; int payloadlength; } struct MessageBuffer { struct Header header; char[MAXSIZE] payload; } class Message { private: MessageBuffer m; public: Message( MessageBuffer buf ) { m = buf; } struct Header GetHeader( ) { return m.header; } char* GetPayLoad( ) { return &m.payload; } }
自从我编写任何C++以来已经有一段时间了,所以请原谅语法方面的任何问题.只是想传达一般的想法.
这是一个常见的问题,但你真正想要的是这个.
class Header { int type; int payloadLength; char payload[0]; }; char* Header::GetPayload() { return payload; }
我的投票是Coding Horror.不要误会我的意思,这很聪明 - 但是你要为自己节省一整个额外的操作,代价是使代码更难以理解和阅读.我不认为这种权衡是值得的.
如果标题需要"返回"未包含在其中的数据,我认为从一开始就存在缺陷.
正如你已经把自己置于这些hackish理由,我真的很喜欢你想出的.
但请注意,这不是一场选美比赛.您应该找到完全不同的解决方案.对于你提出的三个版本的GetPayload(),如果没有你的进一步解释,我不明白到底是怎么回事.