使用std:vector作为低级缓冲区

这里的用法与将read()直接用于C ++ std:vector中相同,但是具有重新分配的作用。

输入文件的大小是未知的,因此当文件大小超过缓冲区大小时,通过加倍缓冲区来重新分配缓冲区。 这是我的代码:

#include <vector>
#include <fstream>
#include <iostream>

int main()
{
    const size_t initSize = 1;
    std::vector<char> buf(initSize); // sizes buf to initSize, so &buf[0] below is valid
    std::ifstream ifile("D:Picturesinput.jpg", std::ios_base::in|std::ios_base::binary);
    if (ifile)
    {
        size_t bufLen = 0;
        for (buf.reserve(1024); !ifile.eof(); buf.reserve(buf.capacity() << 1))
        {
            std::cout << buf.capacity() << std::endl;
            ifile.read(&buf[0] + bufLen, buf.capacity() - bufLen);
            bufLen += ifile.gcount();
        }
        std::ofstream ofile("rebuild.jpg", std::ios_base::out|std::ios_base::binary);
        if (ofile)
        {
            ofile.write(&buf[0], bufLen);
        }
    }
}

程序按照预期打印矢量容量,并将输出文件写入与输入大小相同的BUT,只有在偏移initSize之前输入相同的字节,然后全部为零。

read()使用&buf[bufLen]是一个未定义的行为,但是&buf[0] + bufLen得到正确的写入信息,因为连续分配是有保证的,不是吗? (提供的initSize != 0注意, std::vector<char> buf(initSize); buf大小设为initSize ,是的,如果initSize == 0 ,在我的环境中发生rumtime致命错误。 这也是UB吗? 该标准是否说过有关std :: vector的这种用法?

PS:是的,我知道我们可以先计算文件大小并分配完全相同的缓冲区大小,但在我的项目中,可以预料输入文件总是小于某个SIZE ,因此我可以将initSize设置为SIZE并期望没有开销(如文件大小计算),并仅为“异常处理”使用重新分配。 是的,我知道我可以用size()替换resize()capacity()来替换reserve() ,然后在开销很小的情况下工作(在每次调整大小时都将缓冲区设置为零),但是我仍然希望摆脱任何redundent手术,只是一种偏执......

更新1:

实际上,我们可以从标准中逻辑推导出&buf[0] + bufLen获得正确的位置,请考虑:

std::vector<char> buf(128);
buf.reserve(512);
char* bufPtr0 = &buf[0], *bufPtrOutofRange = &buf[0] + 200;
buf.resize(256); std::cout << "standard guarantees no reallocation" << std::endl;
char* bufPtr1 = &buf[0], *bufInRange = &buf[200]; 
if (bufPtr0 == bufPtr1)
    std::cout << "so bufPtr0 == bufPtr1" << std::endl;
std::cout << "and 200 < buf.size(), standard guarantees bufInRange == bufPtr1 + 200" << std::endl;
if (bufInRange == bufPtrOutofRange)
    std::cout << "finally we have: bufInRange == bufPtrOutofRange" << std::endl;

输出:

standard guarantees no reallocation
so bufPtr0 == bufPtr1
and 200 < buf.size(), standard guarantees bufInRange == bufPtr1 + 200
finally we have: bufInRange == bufPtrOutofRange

这里可以用每个buf.size() <= i < buf.capacity()替换200,并且类似的推导成立。

更新2:

是的,我错过了一些东西......但问题不是连续性(请参阅更新1),甚至无法写入内存失败。 今天,我花了一些时间来研究这个问题,程序得到了正确的地址,将正确的数据写入了保留的内存,但是在下一个reserve()buf被重新分配,并且只有范围[0, buf.size())复制到新的内存。 所以这是整个谜题的答案...

最后注意:如果你的缓冲区填充了一些数据后你不需要重新分配,你可以使用reserve()/capatity()而不是resize()/size() ,但是如果你需要的话,可以使用后者。

例:

const size_t initSize = 32;
std::vector<char> buf(initSize);
buf.reserve(1024*100); // reserve enough space for file reading
std::ifstream ifile("D:Picturesinput.jpg", std::ios_base::in|std::ios_base::binary);
if (ifile)
{
    ifile.read(&buf[0], buf.capacity());  // ok. the whole file is read into buf
    std::ofstream ofile("rebuld.jpg", std::ios_base::out|std::ios_base::binary);
    if (ofile)
    {
        ofile.write(&buf[0], ifile.gcount()); // rebuld.jpg just identical to input.jpg
    }
}
buf.reserve(1024*200); // horror! probably always lose all data in buf after offset initSize

PS:我还没有发现任何权威来源(标准,TC ++ PL等)明确同意或不同意我提出的上述建议。 但是在这里可用的所有实现(VC ++,g ++,ICC)中,上面的例子工作正常。

这里有另一个例子,引用'TC ++ PL,4e'pp 1041,注意函数的第一行使用reserve()而不是resize()

void fill(istream& in, string& s, int max)
// use s as target for low-level input (simplified)
{
    s.reserve(max); // make sure there is enough allocated space
    in.read(&s[0],max);
    const int n = in.gcount(); // number of characters read
    s.resize(n);
    s.shrink_to_fit();  // discard excess capacity
}

reserve实际上并没有为矢量添加空间,它只能确保在调整大小时不需要重新分配。 一旦你知道你实际读入的字节数,你应该使用resize来代替reserve ,然后做一个最终的resize

编辑:所有reserve保证做的是防止迭代器和指针的失效,因为你增加vector的大小直到capacity() 。 不保证保留这些保留字节的内容,除非它们是size()

链接地址: http://www.djcxy.com/p/16327.html

上一篇: Using std:vector as low level buffer

下一篇: FOSUserBundle: what is the point?