可变大小的结构 C++
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/688471/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Variable Sized Struct C++
提问by Unknown
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.
这是在 C++ 中制作可变大小结构的最佳方法吗?我不想使用向量,因为初始化后长度不会改变。
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;
}
Edit: renamed variable names and changed code to be more correct.
编辑:重命名变量名并更改代码以使其更正确。
回答by bk1e
Some thoughts on what you're doing:
关于你在做什么的一些想法:
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 astd::vector
. If you are allocating a verylarge 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.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.
You can never subclass
Packet
. If you did, then the subclass's member variables would overlap with the array.Instead of
malloc
andfree
, you could usePacket* p = ::operator new(size)
and::operator delete(p)
, sincestruct 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 globaloperator new
handles errors using the global new-handler and/or exceptions, if that matters to you.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 implementingstatic 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 expressionPacket* p = new(len) Packet(len)
to allocate a packet. The only benefit I see compared to using globaloperator new
andoperator delete
would be that clients of your code could just calldelete p
instead of::operator delete(p)
. Wrapping the allocation/deallocation in separate functions (instead of callingdelete p
directly) is fine as long as they get called correctly.
使用 C 风格的可变长度结构体习语允许您为每个数据包执行一次免费存储分配,如果
struct Packet
包含std::vector
. 如果您分配一个非常大的数量的数据包,然后进行一半的自由存储分配/释放操作很可能是显著。如果您还进行网络访问,那么等待网络所花费的时间可能会更显着。这个结构代表一个数据包。您是否打算从套接字直接读/写到
struct Packet
? 如果是这样,您可能需要考虑字节顺序。发送数据包时是否必须从主机字节顺序转换为网络字节顺序,在接收数据包时反之亦然?如果是这样,那么您可以在可变长度结构中对数据进行字节交换。如果您将其转换为使用向量,则编写用于序列化/反序列化数据包的方法是有意义的。这些方法会将其传输到/从连续缓冲区传输,同时考虑字节顺序。同样,您可能需要考虑对齐和打包。
你永远不能子类化
Packet
。如果这样做,则子类的成员变量将与数组重叠。取而代之的
malloc
和free
,你可以使用Packet* p = ::operator new(size)
和::operator delete(p)
,因为struct Packet
是一个POD类型,目前不从具有其默认的构造函数和析构函数叫做受益。这样做的(潜在)好处是全局operator new
使用全局新处理程序和/或异常处理错误,如果这对您很重要。可以使可变长度结构惯用语与 new 和 delete 运算符一起使用,但效果不佳。您可以
operator new
通过实现来创建一个接受数组长度的自定义static void* operator new(size_t size, unsigned int bitlength)
,但您仍然需要设置 bitlength 成员变量。如果使用构造函数执行此操作,则可以使用稍微冗余的表达式Packet* p = new(len) Packet(len)
来分配数据包。我看到与使用全球唯一的受益operator new
和operator delete
将是你的代码的客户端可以只调用delete p
代替::operator delete(p)
。delete p
只要它们被正确调用,将分配/解除分配包装在单独的函数中(而不是直接调用)就可以了。
回答by Nils Pipenbrinck
If you never add a constructor/destructor, assignment operators or virtual functions to your structure using malloc/free for allocation is safe.
如果您从不向结构中添加构造函数/析构函数、赋值运算符或虚函数,则使用 malloc/free 进行分配是安全的。
It's frowned upon in c++ circles, but I consider the usage of it okay if you document it in the code.
它在 C++ 圈子里是不受欢迎的,但如果你在代码中记录它,我认为它的用法是可以的。
Some comments to your code:
对您的代码的一些评论:
struct Packet
{
unsigned int bitlength;
unsigned int data[];
};
If I remember right declaring an array without a length is non-standard. It works on most compilers but may give you a warning. If you want to be compliant declare your array of length 1.
如果我没记错的话,声明一个没有长度的数组是非标准的。它适用于大多数编译器,但可能会给您一个警告。如果您想符合要求,请声明您的长度为 1 的数组。
Packet* CreatePacket(unsigned int length)
{
Packet *output = (Packet*) malloc((length+1)*sizeof(unsigned int));
output->bitlength = length;
return output;
}
This works, but you don't take the size of the structure into account. The code will break once you add new members to your structure. Better do it this way:
这有效,但您没有考虑结构的大小。一旦向结构中添加新成员,代码就会中断。最好这样做:
Packet* CreatePacket(unsigned int length)
{
size_t s = sizeof (Packed) - sizeof (Packed.data);
Packet *output = (Packet*) malloc(s + length * sizeof(unsigned int));
output->bitlength = length;
return output;
}
And write a comment into your packet structure definition that data must be the last member.
并在您的数据包结构定义中写入注释,数据必须是最后一个成员。
Btw - allocating the structure and the data with a single allocation is a good thing. You halve the number of allocations that way, and you improve the locality of data as well. This can improve the performance quite a bit if you allocate lots of packages.
顺便说一句 - 使用单个分配分配结构和数据是一件好事。这样,您将分配数量减半,同时也改善了数据的局部性。如果您分配大量包,这可以大大提高性能。
Unfortunately c++ does not provide a good mechanism to do this, so you often end up with such malloc/free hacks in real world applications.
不幸的是,c++ 没有提供一个很好的机制来做到这一点,所以你经常在现实世界的应用程序中遇到这样的 malloc/free hack。
回答by Martin York
This is OK (and was standard practice for C).
这是可以的(并且是 C 的标准做法)。
But this is not a good idea for C++.
This is because the compiler generates a whole set of other methods automatically for you around the class. These methods do not understand that you have cheated.
但这对 C++ 来说不是一个好主意。
这是因为编译器会围绕类自动为您生成一整套其他方法。这些方法不明白你被骗了。
For Example:
例如:
void copyRHSToLeft(Packet& lhs,Packet& rhs)
{
lhs = rhs; // The compiler generated code for assignement kicks in here.
// Are your objects going to cope correctly??
}
Packet* a = CreatePacket(3);
Packet* b = CreatePacket(5);
copyRHSToLeft(*a,*b);
Use the std::vector<> it is much safer and works correctly.
I would also bet it is just as efficient as your implementation after the optimizer kicks in.
使用 std::vector<> 它更安全并且工作正常。
我还敢打赌,在优化器启动后,它与您的实现一样有效。
Alternatively boost contains a fixed size array:
http://www.boost.org/doc/libs/1_38_0/doc/html/array.html
或者 boost 包含一个固定大小的数组:http:
//www.boost.org/doc/libs/1_38_0/doc/html/array.html
回答by Jimmy J
You can use the "C" method if you want but for safety make it so the compiler won't try to copy it:
如果需要,您可以使用“C”方法,但为了安全起见,请使用它以便编译器不会尝试复制它:
struct Packet
{
unsigned int bytelength;
unsigned int data[];
private:
// Will cause compiler error if you misuse this struct
void Packet(const Packet&);
void operator=(const Packet&);
};
回答by Michael Burr
I'd probably just stick with using a vector<>
unless the minimal extra overhead (probably a single extra word or pointer over your implementation) is really posing a problem. There's nothing that says you have to resize() a vector once it's been constructed.
我可能会坚持使用 a ,vector<>
除非最小的额外开销(可能是您的实现上的一个额外单词或指针)确实造成了问题。没有什么说你必须在构造向量后调整大小()。
However, there are several The advantages of going with vector<>
:
但是,使用 with 有几个优点vector<>
:
- it already handles copy, assignment & destruction properly - if you roll your own you need to ensure you handle these correctly
- all the iterator support is there - again, you don't have to roll your own.
- everybody already knows how to use it
- 它已经正确处理了复制、分配和销毁 - 如果你自己动手,你需要确保你正确处理这些
- 所有的迭代器支持都在那里——同样,你不必自己滚动。
- 每个人都已经知道如何使用它
If you really want to prevent the array from growing once constructed, you might want to consider having your own class that inherits from vector<>
privately or has a vector<>
member and only expose via methods that just thunk to the vector methods those bits of vector that you want clients to be able to use. That should help get you going quickly with pretty good assurance that leaks and what not are not there. If you do this and find that the small overhead of vector is not working for you, you can reimplement that class without the help of vector and your client code shouldn't need to change.
如果您真的想防止数组在构造后增长,您可能需要考虑拥有自己的类,该类从vector<>
私有继承或具有vector<>
成员,并且仅通过仅通过向量方法暴露您想要客户端的那些向量位的方法公开能够使用。这应该有助于让你快速前进,并很好地保证泄漏和不存在的东西。如果您这样做并发现 vector 的小开销对您不起作用,您可以在没有 vector 帮助的情况下重新实现该类,并且您的客户端代码不需要更改。
回答by Matt Davis
If you are truly doing C++, there is no practical difference between a class and a struct except the default member visibility - classes have private visibility by default while structs have public visibility by default. The following are equivalent:
如果您真的在使用 C++,那么除了默认成员可见性之外,类和结构之间没有实际区别 - 默认情况下,类具有私有可见性,而默认情况下结构具有公共可见性。以下是等效的:
struct PacketStruct
{
unsigned int bitlength;
unsigned int data[];
};
class PacketClass
{
public:
unsigned int bitlength;
unsigned int data[];
};
The point is, you don't need the CreatePacket(). You can simply initialize the struct object with a constructor.
关键是,您不需要 CreatePacket()。您可以使用构造函数简单地初始化 struct 对象。
struct Packet
{
unsigned long bytelength;
unsigned char data[];
Packet(unsigned long length = 256) // default constructor replaces CreatePacket()
: bytelength(length),
data(new unsigned char[length])
{
}
~Packet() // destructor to avoid memory leak
{
delete [] data;
}
};
A few things to note. In C++, use new instead of malloc. I've taken some liberty and changed bitlength to bytelength. If this class represents a network packet, you'll be much better off dealing with bytes instead of bits (in my opinion). The data array is an array of unsigned char, not unsigned int. Again, this is based on my assumption that this class represents a network packet. The constructor allows you to create a Packet like this:
有几点需要注意。在 C++ 中,使用 new 而不是 malloc。我已经采取了一些自由并将位长度更改为字节长度。如果这个类代表一个网络数据包,你会更好地处理字节而不是位(在我看来)。数据数组是一个无符号字符数组,而不是无符号整数。同样,这是基于我的假设,即此类代表网络数据包。构造函数允许您像这样创建数据包:
Packet p; // default packet with 256-byte data array
Packet p(1024); // packet with 1024-byte data array
The destructor is called automatically when the Packet instance goes out of scope and prevents a memory leak.
当 Packet 实例超出范围并防止内存泄漏时,会自动调用析构函数。
回答by quinmars
There are already many good thoughts mentioned here. But one is missing. Flexible Arrays are part of C99 and thus aren't part of C++, although some C++ compiler may provide this functionality there is no guarantee for that. If you find a way to use them in C++ in an acceptable way, but you have a compiler that doesn't support it, you perhaps can fallback to the "classical" way
这里已经提到了很多好的想法。但是少了一个。灵活数组是 C99 的一部分,因此不是 C++ 的一部分,尽管某些 C++ 编译器可能提供此功能,但不能保证。如果您找到一种以可接受的方式在 C++ 中使用它们的方法,但是您的编译器不支持它,您也许可以退回到“经典”方式
回答by Edouard A.
You probably want something lighter than a vector for high performances. You also want to be very specific about the size of your packet to be cross-platform. But you don't want to bother about memory leaks either.
您可能想要比矢量更轻的东西以获得高性能。您还希望非常具体地了解跨平台的数据包大小。但是您也不想担心内存泄漏。
Fortunately the boost library did most of the hard part:
幸运的是,boost 库完成了大部分困难的工作:
struct packet
{
boost::uint32_t _size;
boost::scoped_array<unsigned char> _data;
packet() : _size(0) {}
explicit packet(packet boost::uint32_t s) : _size(s), _data(new unsigned char [s]) {}
explicit packet(const void * const d, boost::uint32_t s) : _size(s), _data(new unsigned char [s])
{
std::memcpy(_data, static_cast<const unsigned char * const>(d), _size);
}
};
typedef boost::shared_ptr<packet> packet_ptr;
packet_ptr build_packet(const void const * data, boost::uint32_t s)
{
return packet_ptr(new packet(data, s));
}
回答by Paul Nathan
You should declare a pointer, not an array with an unspecified length.
您应该声明一个指针,而不是一个未指定长度的数组。
回答by T.E.D.
There's nothing whatsoever wrong with using vector for arrays of unknown size that will be fixed after initialization. IMHO, that's exactly what vectors are for. Once you have it initialized, you can pretend the thing is an array, and it should behave the same (including time behavior).
对未知大小的数组使用向量并没有任何问题,这些数组将在初始化后修复。恕我直言,这正是矢量的用途。一旦你初始化了它,你就可以假装它是一个数组,它的行为应该是一样的(包括时间行为)。