我不是一个C ++新手,但我过去几乎没有认真对待它,所以我对它的设施的了解相当粗略。
我正在用C ++编写一个快速概念验证程序,我需要一个动态大小的二进制数据缓冲区。也就是说,我将从网络套接字接收数据,我不知道会有多少(虽然不超过几MB)。我自己可以编写这样的缓冲区,但是为什么标准库可能已经有了什么呢?我正在使用VS2008,因此一些特定于Microsoft的扩展程序对我来说很好。我只需要四个操作:
我需要的类/功能集的名称是什么?
已添加:多次投票转至std::vector
。一切都很好,但我不想逐字节地推送几MB的数据。套接字将以几KB大块的形式向我提供数据,所以我想一次性写入它们。另外,最后我需要将数据作为一个简单的char *,因为我需要将整个blob传递给一些未经修改的Win32 API函数。
答案 0 :(得分:39)
您需要std::vector
:
std::vector<char> myData;
vector
会自动为您分配和释放内存。使用push_back
添加新数据(vector
将根据需要为您调整大小),并使用索引运算符[]
来检索数据。
如果您在任何时候可以猜到您需要多少内存,我建议您调用reserve
,以便后续的push_back
不必再重新分配。
如果你想读入一大块内存并将其附加到缓冲区,最简单的可能就是:
std::vector<char> myData;
for (;;) {
const int BufferSize = 1024;
char rawBuffer[BufferSize];
const unsigned bytesRead = get_network_data(rawBuffer, sizeof(rawBuffer));
if (bytesRead <= 0) {
break;
}
myData.insert(myData.end(), rawBuffer, rawBuffer + bytesRead);
}
myData
现在拥有所有读取数据,按块读取块。但是,我们要复制两次。
我们尝试这样的事情:
std::vector<char> myData;
for (;;) {
const int BufferSize = 1024;
const size_t oldSize = myData.size();
myData.resize(myData.size() + BufferSize);
const unsigned bytesRead = get_network_data(&myData[oldSize], BufferSize);
myData.resize(oldSize + bytesRead);
if (bytesRead == 0) {
break;
}
}
它直接读入缓冲区,但偶尔会过度分配。
这可以通过例如将每个调整大小的矢量大小加倍以分摊调整大小,因为第一个解决方案隐式执行。当然,如果您对最终缓冲区的可能大小有先验知识,则可以预先reserve()
更大的缓冲区,以最小化调整大小。
两者都留给读者练习。 :)
最后,如果您需要将数据视为原始数组:
some_c_function(myData.data(), myData.size());
std::vector
保证是连续的。
答案 1 :(得分:9)
std::vector<unsigned char> buffer;
每个push_back都会在最后添加新的char(如果需要,可以重新分配)。如果您大致知道预期的数据量,可以调用reserve来最小化分配数量。
buffer.reserve(1000000);
如果你有这样的事情:
unsigned char buffer[1000];
std::vector<unsigned char> vec(buffer, buffer + 1000);
答案 2 :(得分:7)
std::string
适用于此:
append()
来向其附加多字节数据块。data()
来获取其内容作为字符数组,并通过调用size()
或length()
来获取当前长度。clear()
来删除其内容而不会破坏它。答案 3 :(得分:6)
再次投票给std :: vector。最小的代码,跳过额外的副本GMan的代码:
std::vector<char> buffer;
static const size_t MaxBytesPerRecv = 1024;
size_t bytesRead;
do
{
const size_t oldSize = buffer.size();
buffer.resize(oldSize + MaxBytesPerRecv);
bytesRead = receive(&buffer[oldSize], MaxBytesPerRecv); // pseudo, as is the case with winsock recv() functions, they get a buffer and maximum bytes to write to the buffer
myData.resize(oldSize + bytesRead); // shrink the vector, this is practically no-op - it only modifies the internal size, no data is moved/freed
} while (bytesRead > 0);
至于调用WinAPI函数 - 使用&amp; buffer [0](是的,它有点笨拙,但这就是它的方式)传递给char *参数,buffer.size()作为长度。
最后一点,您可以使用std :: string而不是std :: vector,应该没有任何区别(除非您可以编写buffer.data()而不是&amp; buffer [0]是一个字符串)
答案 4 :(得分:4)
我会看看Boost basic_streambuf,它是为这种目的而设计的。如果你不能(或不想)使用Boost,我会考虑std::basic_streambuf
,它非常相似,但需要更多的工作。无论哪种方式,您基本上都是从该基类派生并重载underflow()
以将数据从套接字读入缓冲区。您通常会将std::istream
附加到缓冲区,因此其他代码的读取方式与用户从键盘输入的方式相同(或其他)。
答案 5 :(得分:2)
不是来自STL但可能有用的替代方案 - Boost.Circular buffer
答案 6 :(得分:1)
使用std::vector,一个不断增长的数组,保证存储是连续的(你的第三点)。
答案 7 :(得分:0)
关于你的评论“我没有看到附加()”,最后的是相同的。
vec.insert(vec.end,
答案 8 :(得分:0)
如果您使用std :: vector,那么您只是用它来管理原始内存。
您可以只malloc
您认为需要的最大缓冲区,并跟踪到目前为止读取的写入偏移量/总字节数(它们是相同的)。
如果你到了最后...... realloc
或选择失败的方法。
我知道,它不是非常C ++,但这是一个简单的问题,其他提案似乎是引入不必要副本的重量级方法。
答案 9 :(得分:0)
这里的重点是,您想将缓冲区用于什么目的。 如果要保留带有指针的结构,则必须将缓冲区固定在首先分配的内存地址处。 为了避免这种情况,您必须使用相对指针和修复列表来在最终分配后更新指针。这将值得一堂课。 (没找到这样的东西)