SRo*_*mes 25 c++ io memory-management
对于I/O工作,我需要将N个字节读入缓冲区.N在运行时(不是编译时)已知.缓冲区大小永远不会改变.缓冲区被传递给其他例程进行压缩,加密等:它只是一个字节序列,没有比这更高的值.
在C中,我将分配缓冲区,malloc
然后free
在完成后分配缓冲区.但是,我的代码是现代的C++,当然没有malloc
到位,而且很少有原始new
和delete
:我正在大量使用RAII和shared_ptr
.但是,这些技术似乎都不适合这种缓冲区.它只是一个固定长度的字节缓冲区,用于接收I/O并使其内容可用.
这是优雅的现代C++成语吗?或者,对于这方面,我应该坚持使用好的' malloc
?
lis*_*rus 15
基本上,您有两种主要的C++方式选择:
std::vector
std::unique_ptr
我更喜欢第二种,因为你不需要所有自动调整大小的东西std::vector
,而你不需要容器 - 你只需要一个缓冲区.
std::unique_ptr
有一个动态数组专门化:std::unique_ptr<int[]>
将调用delete []
它的析构函数,并将为您提供适当的operator []
.
如果你想要代码:
std::unique_ptr<char[]> buffer(new char [size]);
some_io_function(buffer.get(), size); // get() returnes raw pointer
Run Code Online (Sandbox Code Playgroud)
不幸的是,它无法检索缓冲区的大小,因此您必须将其存储在变量中.如果它让你困惑,那么std::vector
就会做的工作:
std::vector<char> buffer(size);
some_io_function(buffer.data(), buffer.size()); // data() returnes raw pointer
Run Code Online (Sandbox Code Playgroud)
如果要传递缓冲区,则取决于您的具体操作方式.
考虑以下情况:缓冲区在某处填充,然后在其他地方处理,存储一段时间,然后写入某处并销毁.碰巧你从来没有真正需要代码中的两个位置来拥有缓冲区,你可以简单地std::move
从一个地方到另一个地方.对于这个用例,std::unique_ptr
将完美地工作,并将保护您免于偶尔复制缓冲区(同时std::vector
您可以错误地复制它,并且不会出现错误或警告).
相反,如果你需要代码中的几个位置来保存相同的缓冲区(可能它在多个地方同时填充/使用/处理),你肯定需要std::shared_ptr
.不幸的是,它没有类似数组的特化,所以你必须传递适当的删除器:
std::shared_ptr<char> buffer(new char[size], std::default_delete<char[]>());
Run Code Online (Sandbox Code Playgroud)
第三个选项是如果你真的需要复制缓冲区.然后,std::vector
会更简单.但是,正如我已经提到的,我觉得这不是最好的方式.此外,您始终可以手动std::unique_ptr
或std::shared_ptr
手动复制缓冲区,这清楚地表明了您的意图:
std::uniqure_ptr<char[]> buffer_copy(new char[size]);
std::copy(buffer.get(), buffer.get() + size, buffer_copy.get());
Run Code Online (Sandbox Code Playgroud)
在C++ 14中,有一种非常语法清晰的方式来实现你想要的东西:
size_t n = /* size of buffer */;
auto buf_ptr = std::make_unique<uint8_t[]>(n);
auto nr = ::read(STDIN_FILENO, buf_ptr.get(), n);
auto nw = ::write(STDOUT_FILENO, buf_ptr.get(), nr);
// etc.
// buffer is freed automatically when buf_ptr goes out of scope
Run Code Online (Sandbox Code Playgroud)
请注意,上述构造将对缓冲区进行值初始化(清零).如果你想跳过初始化以保存几个周期,你将不得不使用lisyarus给出的略微丑陋的形式:
std::unique_ptr<uint8_t[]> buf_ptr(new uint8_t[n]);
Run Code Online (Sandbox Code Playgroud)
我认为使用std :: vector是常见的.
在手动分配的缓冲区上使用std :: vector的好处char
是复制语义(用于传递给希望为自己的目的修改数据或将数据返回给调用函数的函数).
又一个的std ::矢量知道自己的大小减少需要传递给处理功能和消除错误的源参数的数量.
您可以完全控制数据如何传递给其他函数 - 通过引用或const引用(如果适用).
如果您需要调用具有普通和长度的旧C风格函数,char*
您也可以轻松地执行此操作:
// pass by const reference to preserve data
void print_data(const std::vector<char>& buf)
{
std::cout.fill('0');
std::cout << "0x";
for(auto c: buf)
std::cout << std::setw(2) << std::hex << int(c);
std::cout << '\n';
}
// pass by reference to modify data
void process_data(std::vector<char>& buf)
{
for(auto& c: buf)
c += 1;
}
// pass by copy to modify data for another purpose
void reinterpret_data(std::vector<char> buf)
{
// original data not changed
process_data(buf);
print_data(buf);
}
void legacy_function(const char* buf, std::size_t length)
{
// stuff
}
int main()
{
std::ifstream ifs("file.txt");
// 24 character contiguous buffer
std::vector<char> buf(24);
while(ifs.read(buf.data(), buf.size()))
{
// changes data (pass by reference)
process_data(buf);
// modifies data internally (pass by value)
reinterpret_data(buf);
// non-modifying function (pass by const ref)
print_data(buf);
legacy_function(buf.data(), buf.size());
}
}
Run Code Online (Sandbox Code Playgroud)