Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/145.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 调整C+的大小+;标准::向量<;char>;不初始化数据_C++_Stl_Vector_Resize - Fatal编程技术网

C++ 调整C+的大小+;标准::向量<;char>;不初始化数据

C++ 调整C+的大小+;标准::向量<;char>;不初始化数据,c++,stl,vector,resize,C++,Stl,Vector,Resize,对于向量,可以假设元素连续存储在内存中,允许范围[&vec[0]、&vec[vec.capacity())用作普通数组。例如 vector<char> buf; buf.reserve(N); int M = read(fd, &buf[0], N); 这是可行的(这就是我今天所做的),但如果我可以将数据直接放入向量中,就不需要额外的复制操作,这让我很烦恼 那么,在外部添加数据时,是否有一种简单的方法来修改向量大小 vector<char> buf; buf.r

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

vector<char> buf;
buf.reserve(N);
int M = read(fd, &buf[0], N);
这是可行的(这就是我今天所做的),但如果我可以将数据直接放入向量中,就不需要额外的复制操作,这让我很烦恼

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

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


注:您的语句“对于向量,可以假设元素连续存储在内存中,允许范围
[&vec[0],&vec[vec.capacity())
用作普通数组”是不正确的。允许的范围是
[&vec[0],&vec[vec.size())
您的程序片段已进入未定义行为的领域

buf.empty()
为true时,
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
vectorbuf;
buf.resize(N);//预先分配空间
int M=读取(fd,&buf[0],N);
buf.resize(M);//不允许访问其余部分

写入
size()
th元素及其后是一种未定义的行为

下一个示例将C++中的整个文件复制到一个向量中(不需要知道文件的大小,也不需要预先分配向量中的内存):

#包括
#包括
#包括
#包括
int main()
{
类型定义标准::istream_迭代器istream_迭代器;
std::ifstream文件(“example.txt”);
向量输入;
文件>>std::noskipws;
复制(istream_迭代器(文件),
istream_迭代器(),
标准:背向插入器(输入);
}

看起来您可以在C++11中做您想做的事情(尽管我自己没有尝试过)。您必须为向量定义一个自定义分配器,然后使用
emplace\u 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);
std::vector buf;
基本储备(N);
对于(int i=0;i!=N;++i)
buf.emplace_back(不初始化标签());
int M=读取(fd,buf.data(),N);
buf.调整大小(M);

这个问题的效率取决于编译器的优化器。例如,循环可能会将大小成员变量增加N倍。

另一个较新的问题,与此问题的一个副本,看起来与此处的问题完全相同。这是它的副本(v3),供快速参考:

这是一个已知的问题,初始化甚至不能关闭 明确用于
std::vector

人们通常实现他们自己的
pod_向量
,但这不起作用 元素的任何初始化

另一种方法是创建一个与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();
}
struct NoInitChar
{
字符值;
NoInitChar(){
//无所事事
静态_断言(sizeof*this==sizeof值,“无效大小”);
static_assert(uuu alignof*this==uuu alignof value,“无效对齐”);
}
};
int main(){
std::向量v;
v、 调整大小(10);//调用不初始化的NoInitChar()
//听着,妈,不要再解释了!
char*beg=&v.front().value;
char*end=beg+v.size();
}

无法避免第一次resize()导致的不必要的初始化?99%的确定度,额外的初始化将与I/O成本相形见绌。@user984228:问题是这是否是一个问题。如果您进行了测量,并且初始化成为一个瓶颈(我不希望这样)然后,您可能需要考虑实现自己的数据结构……注意:如果且仅当,我不想让您实现自己的数据类型,而是认识到在大多数情况下,这将不是性能瓶颈——即,无论您从何处读取,都可能比初始化的成本慢得多。您期望insert(范围)的良好实现要专门使用随机迭代器的单个保留调用?@user984228:
那么我宁愿使用临时缓冲区+插入。它至少应该同样有效,
不正确。临时缓冲区避免零初始化,读入缓冲区,然后需要从缓冲区到向量的副本。向量大小调整的初始值为零零初始化至少和副本一样快,可能比副本快。因此,调整大小仍然比缓冲区快。您确定
&buf[0]
在调试模式下工作吗?例如,在Visual Studio上,在调试模式下
std::vector::operator[]
执行范围检查。因此,如果
buf
为空,表达式将抛出。我使用GCC,并通过valgrind运行程序以确保没有内存错误发生。我所能说的是,在GNU libstdc++实现中,这是可行的。&vec[0]似乎为您提供了一个指向保留内存的直接指针,无论大小()@user984228:如果你乐于依赖GCC的实现细节(这是个坏主意(TM)),然后您将查看其实现
向量的源代码。您可以看到它存储
开始
结束
指针和容量的位置,如果您只是覆盖
结束
指针,我敢肯定这将根据您的需要更改大小。只要复制
调整大小()的任何实现即可
在容量足够大的情况下可以开始使用,不包括memset/fill/任何东西。当然,你必须使用一些
私有的
修饰符,也许是通过在偏移量中进行硬编码。@SteveJessop:我只是死了一点。@Matthieu:没错。如果所有这些听起来都是个坏主意,那么希望依靠事实上,GCC似乎允许您写入只保留的空间,而不是大小调整的空间,这听起来也很糟糕
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);
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();
}