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

use*_*228 35 c++ stl resize vector

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

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

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

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

char tmp[N];
int M = read(fd, tmp, N);
buf.insert(buf.end(), tmp, tmp + M)
Run Code Online (Sandbox Code Playgroud)

这是有效的(这就是我今天正在做的事情),但是如果我可以将数据直接放入向量中,那么在那里会有一个额外的复制操作,这是困扰我的.

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

Rob*_*obᵩ 24

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

此代码片段调用未定义的行为.即使您保留了空间size(),也不能超出元素.

正确的代码如下:

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


PS.您的声明"使用向量,可以假设元素连续存储在内存中,允许将范围[&vec[0], &vec[vec.capacity())用作普通数组"不成立.允许的范围是[&vec[0], &vec[vec.size()).

  • 有没有办法避免第一个resize()导致的不必要的初始化? (8认同)
  • 99%的确定性,无论如何,额外的初始化将与I/O的成本相形见绌. (3认同)
  • @ user984228:`那我宁愿只使用临时缓冲区+插入.它应该至少同样有效,"不正确".临时缓冲区避免零初始化,读入缓冲区,然后需要从缓冲区到向量的复制.Vector resize初始化为零,然后读入向量.零初始化与复制一样快,可能更快.因此,调整大小仍然比缓冲区更快. (3认同)
  • @ user984228:问题是这是否是一个问题.如果你已经测量并且初始化成为瓶颈(我不希望这样)那么你可能需要考虑实现自己的数据结构...注意:*当且仅当*,我不是试图让你实现自己的数据类型,而是意识到在大多数情况下不会成为性能瓶颈 - 无论你在哪里阅读,都可能比初始化的成本慢得多. (2认同)
  • @MarkB:你不期望插入(范围)的良好实现专门用于随机迭代器的单个保留调用吗? (2认同)

Der*_*ter 7

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

首先,定义

struct do_not_initialize_tag {};
Run Code Online (Sandbox Code Playgroud)

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

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

    // details omitted
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)

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


Rus*_*lan 6

另一个较新的问题(与该问题重复)具有答案,看起来与此处的要求完全相同。这是(v3)的副本,以供快速参考:

一个众所周知的问题是,即使为明确也不能关闭初始化std::vector

人们通常实现自己pod_vector<>的元素,而不对元素进行任何初始化。

另一种方法是创建与char布局兼容的类型,其构造函数不执行任何操作:

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();
}
Run Code Online (Sandbox Code Playgroud)