用于分配/解除分配I / O缓冲区的现代C ++习惯用法

时间:2015-06-02 11:35:08

标签: c++ memory-management io

对于I / O工作,我需要将N个字节读入缓冲区。 N在运行时(不是编译时)已知。缓冲区大小永远不会改变。缓冲区被传递给其他例程进行压缩,加密等:它只是一个字节序列,没有比这更高的。

在C中,我会用free分配缓冲区,然后在完成后分配malloc缓冲区。但是,我的代码是现代C ++,当然没有new,而且很少有原始deleteshared_ptr:我正在大量使用RAII和malloc。但是,这些技术似乎都不适合这种缓冲区。它只是一个固定长度的字节缓冲区,用于接收I / O并使其内容可用。

这是优雅的现代C ++成语吗?或者,对于这方面,我应该坚持使用好的on Ubuntu run:>./manage.py runserver 0.0.0.0:8000 > /dev/null 2>&1 & >exit

5 个答案:

答案 0 :(得分:15)

基本上,您有两种主要的C ++方式选择:

  • std::vector
  • std::unique_ptr

我更喜欢第二种,因为你不需要std::vector中所有自动调整大小的内容,而你不需要容器 - 你只需要一个缓冲区。 / p>

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

不幸的是,它没有办法检索缓冲区的大小,所以你必须将它存储在一个变量中。如果它让您感到困惑,那么std::vector将完成工作:

std::vector<char> buffer(size);
some_io_function(buffer.data(), buffer.size()); // data() returnes raw pointer

如果您想要传递缓冲区,则取决于您的具体操作方式。

考虑以下情况:缓冲区在某处填充,然后在其他地方处理,存储一段时间,然后写入某处并销毁。碰巧你从来没有真正需要代码中的两个位置来拥有缓冲区,你可以简单地std::move从一个地方到另一个地方。对于这个用例,std::unique_ptr将完美地工作,并且可以防止您偶尔复制缓冲区(使用std::vector时,您可以错误地复制它,并且不会出现错误或警告)。

相反,如果代码中需要多个位置来保存相同的缓冲区(可能同时在多个地方填充/使用/处理),您肯定需要std::shared_ptr。不幸的是,它没有类似数组的特化,所以你必须通过适当的删除器:

std::shared_ptr<char> buffer(new char[size], std::default_delete<char[]>());

第三个选项是你真的需要复制缓冲区。然后,std::vector会更简单。但是,正如我已经提到的那样,我觉得这不是最好的方式。此外,您始终可以手动复制std::unique_ptrstd::shared_ptr缓冲区,这清楚地记录了您的意图:

std::uniqure_ptr<char[]> buffer_copy(new char[size]);
std::copy(buffer.get(), buffer.get() + size, buffer_copy.get());

答案 1 :(得分:10)

是的,简单:

std::vector<char> myBuffer(N);

答案 2 :(得分:9)

在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

请注意,上述构造将对缓冲区进行值初始化(清零)。如果你想跳过初始化以节省几个周期,你将不得不使用lisyarus给出的略微丑陋的形式:

std::unique_ptr<uint8_t[]> buf_ptr(new uint8_t[n]);

答案 3 :(得分:5)

我认为使用std::vector就可以了。

在手动分配的char缓冲区上使用std::vector的好处是复制语义(用于传递给希望为自己的目的修改数据或将数据返回给调用函数的函数)

同样,std::vector知道自己的大小,减少了需要传递给处理函数和消除错误源的参数数量。

您可以完全控制数据如何传递给其他函数 - 可以选择 reference const reference

如果您需要使用普通char*和长度调用较旧的 c风格的函数,您也可以轻松地执行此操作:

// 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());
    }
}

答案 4 :(得分:0)

使用

std::vector<char> buffer(N)

如果缓冲区的大小永远不会改变,可以通过这样做将其用作数组:

char * bufPtr = &buffer[0];

这也适用于C ++ 03。有关此安全原因的详细信息,请参阅此评论https://stackoverflow.com/a/247764/1219722