可变大小的结构 C++

2021-12-23 00:00:00 struct c++ variable-length

这是在 C++ 中创建可变大小结构的最佳方法吗?我不想使用vector,因为初始化后长度不会改变.

Is this the best way to make a variable sized struct in C++? I don't want to use vector because the length doesn't change after initialization.

struct Packet
{
    unsigned int bytelength;
    unsigned int data[];
};

Packet* CreatePacket(unsigned int length)
{
    Packet *output = (Packet*) malloc((length+1)*sizeof(unsigned int));
    output->bytelength = length;
    return output;
}

重命名变量名并更改代码以使其更正确.

renamed variable names and changed code to be more correct.

推荐答案

关于你正在做的事情的一些想法:

Some thoughts on what you're doing:

  • 使用 C 风格的可变长度结构体习语允许您为每个数据包执行一次免费存储分配,如果 struct Packet 包含一个 ,则所需数量是所需数量的一半std::vector.如果您要分配非常 数量的数据包,那么执行一半的空闲存储分配/解除分配可能非常重要.如果您还进行网络访问,那么等待网络所花费的时间可能会更显着.

  • Using the C-style variable length struct idiom allows you to perform one free store allocation per packet, which is half as many as would be required if struct Packet contained a std::vector. If you are allocating a very large number of packets, then performing half as many free store allocations/deallocations may very well be significant. If you are also doing network accesses, then the time spent waiting for the network will probably be more significant.

这个结构代表一个数据包.您是否打算从套接字直接读/写到 struct Packet 中?如果是这样,您可能需要考虑字节顺序.发送数据包时是否必须从主机字节顺序转换为网络字节顺序,在接收数据包时反之亦然?如果是这样,那么您可以在可变长度结构中对数据进行字节交换.如果您将其转换为使用向量,则编写用于序列化/反序列化数据包的方法是有意义的.这些方法会将其传输到/从连续缓冲区传输,同时考虑字节顺序.

This structure represents a packet. Are you planning to read/write from a socket directly into a struct Packet? If so, you probably need to consider byte order. Are you going to have to convert from host to network byte order when sending packets, and vice versa when receiving packets? If so, then you could byte-swap the data in place in your variable length struct. If you converted this to use a vector, it would make sense to write methods for serializing / deserializing the packet. These methods would transfer it to/from a contiguous buffer, taking byte order into account.

同样,您可能需要考虑对齐和打包.

Likewise, you may need to take alignment and packing into account.

你永远不能子类化Packet.如果这样做,则子类的成员变量将与数组重叠.

You can never subclass Packet. If you did, then the subclass's member variables would overlap with the array.

代替 mallocfree,你可以使用 Packet* p = ::operator new(size)>::operator delete(p),因为 struct Packet 是一种 POD 类型,目前无法从调用其默认构造函数和析构函数中受益.这样做的(潜在)好处是全局 operator new 使用全局 new 处理程序和/或异常处理错误(如果这对您很重要).

Instead of malloc and free, you could use Packet* p = ::operator new(size) and ::operator delete(p), since struct Packet is a POD type and does not currently benefit from having its default constructor and its destructor called. The (potential) benefit of doing so is that the global operator new handles errors using the global new-handler and/or exceptions, if that matters to you.

可以使变长结构惯用语与 new 和 delete 运算符一起使用,但效果不佳.您可以通过实现 static void* operator new(size_t size, unsigned int bitlength) 创建一个自定义的operator new,它采用数组长度,但您仍然必须设置位长成员变量.如果您使用构造函数执行此操作,则可以使用稍微冗余的表达式 Packet* p = new(len) Packet(len) 来分配数据包.与使用全局 operator newoperator delete 相比,我看到的唯一好处是代码的客户端可以调用 delete p 而不是 <代码>::操作符删除(p).将分配/解除分配包装在单独的函数中(而不是直接调用 delete p)就可以了,只要它们被正确调用即可.

It is possible to make the variable length struct idiom work with the new and delete operators, but not well. You could create a custom operator new that takes an array length by implementing static void* operator new(size_t size, unsigned int bitlength), but you would still have to set the bitlength member variable. If you did this with a constructor, you could use the slightly redundant expression Packet* p = new(len) Packet(len) to allocate a packet. The only benefit I see compared to using global operator new and operator delete would be that clients of your code could just call delete p instead of ::operator delete(p). Wrapping the allocation/deallocation in separate functions (instead of calling delete p directly) is fine as long as they get called correctly.

相关文章