在不初始化数据的情况下调整C ++ std :: vector <char>的大小</char>

时间:2011-10-07 15:27:33

标签: c++ stl vector resize

使用向量,可以假设元素连续存储在内存中,允许范围 [&amp; vec [0],&amp; vec [vec.capacity())用作正常数组。如,

vector<char> buf;
buf.reserve(N);
int M = read(fd, &buf[0], N);

但现在该向量不知道它包含 M 字节的数据,由 read()外部添加。我知道 vector :: resize()设置了大小,但它也清除了数据,所以它不能用于在 read()之后更新大小调用

是否有一种简单的方法将数据直接读入矢量并更新后的大小?是的,我知道明显的解决方法,例如使用小数组作为临时读缓冲区,并使用 vector :: insert()将其附加到向量的末尾:

char tmp[N];
int M = read(fd, tmp, N);
buf.insert(buf.end(), tmp, tmp + M)

这是有效的(而且这就是我今天所做的),但是如果我可以直接将数据直接放入中,那么我就不会需要额外的复制操作。矢量。

那么,在外部添加数据时,是否有一种简单的方法来修改矢量大小?

6 个答案:

答案 0 :(得分:24)

vector<char> buf;
buf.reserve(N);
int M = read(fd, &buf[0], N);

此代码片段调用未定义的行为。即使您保留了空格,也不能写出超过size()元素的内容。

正确的代码如下:

vector<char> buf;
buf.resize(N);
int M = read(fd, &buf[0], N);
buf.resize(M);

<小时/> PS。您的语句“使用向量,可以假设元素连续存储在内存中,允许范围 [&vec[0], &vec[vec.capacity()) 用作普通数组”isn不行。允许的范围是 [&vec[0], &vec[vec.size())

答案 1 :(得分:7)

看起来你可以在C ++ 11中做你想做的事情(虽然我自己没有尝试过)。您必须为向量定义自定义分配器,然后使用emplace_back()

首先,定义

struct do_not_initialize_tag {};

然后使用此成员函数定义分配器:

class my_allocator {
    void construct(char* c, do_not_initialize_tag) const {
        // do nothing
    }

    // details omitted
    // ...
}

现在您可以在不初始化数组的情况下向元素添加元素:

std::vector<char, my_allocator> buf;
buf.reserve(N);
for (int i = 0; i != N; ++i)
    buf.emplace_back(do_not_initialize_tag());
int M = read(fd, buf.data(), N);
buf.resize(M);

这取决于编译器的优化器。例如,循环可以将size成员变量增加N次。

答案 2 :(得分:2)

size()元素之后和之后写入是未定义的行为。

下一个示例以c ++方式将整个文件复制到一个向量中(无需知道文件的大小,也无需在向量中预先分配内存):

#include <algorithm>
#include <fstream>
#include <iterator>
#include <vector>

int main()
{
    typedef std::istream_iterator<char> istream_iterator;
    std::ifstream file("example.txt");
    std::vector<char> input;

    file >> std::noskipws;
    std::copy( istream_iterator(file), 
               istream_iterator(),
               std::back_inserter(input));
}

答案 3 :(得分:2)

另一个更新的问题是这个问题的副本,有an answer,看起来就像这里问的那样。这是其副本(第3版)以供快速参考:

  

已知问题是即使初始化也无法关闭   明确针对struct NoInitChar { char value; NoInitChar() { // do nothing static_assert(sizeof *this == sizeof value, "invalid size"); static_assert(__alignof *this == __alignof value, "invalid alignment"); } }; int main() { std::vector<NoInitChar> v; v.resize(10); // calls NoInitChar() which does not initialize // Look ma, no reinterpret_cast<>! char* beg = &v.front().value; char* end = beg + v.size(); }

     

人们通常会实施自己的{{1}}   任何元素的初始化。

     

另一种方法是创建一个与char布局兼容的类型,   其构造函数什么都不做:

{{1}}

答案 4 :(得分:1)

您的程序片段已进入未定义行为领域。

buf.empty()为真时,buf[0]具有未定义的行为,因此&buf[0]也未定义。

这个片段可能会做你想要的。

vector<char> buf;
buf.resize(N); // preallocate space
int M = read(fd, &buf[0], N);
buf.resize(M); // disallow access to the remainder

答案 5 :(得分:0)

您可能希望尝试子类化vector类并实现一个通过直接访问成员变量来设置大小的方法:在GNU C ++中,这可以通过以下代码片段完成:

template <typename T, typename A = mmap_allocator<T> >
class mmappable_vector: public std::vector<T, A> {
public:
    void map_into_memory(const size_t n)
    {
        std::vector<T,A>::reserve(n);
#ifdef __GNUC__
        std::vector<T,A>::_M_impl._M_finish = std::vector<T,A>::_M_impl._M_end_of_storage;
#else
#error "Not GNU C++, please either implement me or use GCC"
#endif
    }
};

并使用此类(您必须添加一些构造函数)而不是stl :: vector。 但是,除非您处理大于1GB的文件,否则不值得付出努力。

如果你这样做,你可能想尝试一下mmap_allocator(仍然是测试版,可从github.com/johannesthoma/mmap_allocator获得)。