HOWTO · C++
在 C++ 中复制矢量容器对象
本文演示了如何在 C++ 中复制矢量容器对象。
本页内容
复制 C++ std::vector 时,新建向量可使用复制构造函数;要替换现有向量的内容,则使用复制赋值。复制指定范围时,使用范围构造函数。目标范围已有足够空间时可使用 std::copy;若要向目标向量追加元素,则使用 std::back_inserter。这些操作会根据元素类型复制元素,不会递归复制指针所指向的对象。
使用复制构造函数或赋值运算符复制向量
复制初始化会根据现有向量构造一个新向量:std::vector<int> copy = source;。直接初始化也会执行相同操作,只是使用圆括号:std::vector<int> copy(source);。如果目标向量已经存在,则使用赋值:destination = source;。赋值会替换目标向量中的元素。使用这些形式时,请包含 <vector>。
运行此示例:
本节中的代码以及显示的标签或输出仍为英文。
#include <cstddef>
#include <iostream>
#include <vector>
int main() {
const std::vector<int> source{2, 4, 6};
std::vector<int> copy = source;
std::vector<int> destination{9};
destination = source;
for (std::size_t i = 0; i < copy.size(); ++i) {
if (i != 0) {
std::cout << ' ';
}
std::cout << copy[i];
}
std::cout << '\n';
for (std::size_t i = 0; i < destination.size(); ++i) {
if (i != 0) {
std::cout << ' ';
}
std::cout << destination[i];
}
std::cout << '\n';
}
2 4 6
2 4 6
此程序会将每个复制后的向量分别输出到一行。这里的 std::vector<int> 元素是值,因此修改一个向量中的整数,不会改变另一个向量中对应的整数。
使用向量范围构造函数复制一个范围
范围构造函数接收一个半开迭代器范围:包含第一个迭代器所指向的元素,不包含第二个迭代器所指向的元素。使用圆括号可以将此构造函数与初始化列表语法区分开来。
std::vector<int> copy(source.begin(), source.end()); 会复制整个范围。若要复制子范围,并且这些迭代器指向所需元素,可使用 std::vector<int> middle(source.begin() + 1, source.end() - 1);。
第一个声明会复制所有元素;在此示例中,第二个声明会复制中间部分。两个迭代器都必须指向源向量中的有效范围。需要新建目标向量时,无论是复制整个向量还是复制其中一段,这种方式都很方便。
使用 std::copy 复制一个范围
std::copy 会将元素写入现有的目标范围。请包含 <algorithm>,并在复制前确保目标范围的元素数量至少与源范围相同。该算法本身不会扩展向量。
先使用 std::vector<int> destination(source.size());,再调用 std::copy(source.begin(), source.end(), destination.begin());,即可为源范围提供足够大的目标范围。
复制子范围时,传入该范围的起始和结束迭代器,并确保目标向量中有足够的元素容纳它。源范围和目标范围不得以违反算法要求的方式重叠。
使用 std::back_inserter 追加复制的元素
若要追加元素并让向量按需增长,请包含 <algorithm> 和 <iterator>,并使用 std::back_inserter:
例如,使用 std::vector<int> destination{10};,再调用 std::copy(source.begin(), source.end(), std::back_inserter(destination));,即可追加源向量中的元素。
原有的 10 会保留在开头,后面接着源向量中的值。当目标向量已经包含元素,或事先不打算确定其最终大小时,这种写法很有用。
使用循环和 push_back 复制元素
循环能明确展示逐个处理元素的过程。push_back 会将每个复制的值添加到末尾,因此已有的目标向量会继续扩展。
声明 std::vector<int> destination; 后,可使用以下循环:for (const int& value : source) { destination.push_back(value); }。
一般情况下,使用向量复制构造函数或范围构造函数通常更清晰。如果添加元素时还需要逐个转换或筛选,循环就很合适。与标准向量操作一样,循环会根据元素类型复制每个元素。
使用 assign 和 insert 替换或插入目标元素
如果要用一个范围替换目标向量的内容,请使用 assign(first, last)。如果要将源范围插入指定位置,同时保留目标向量中的其他元素,请使用 insert(position, first, last)。
使用 destination.assign(source.begin(), source.end()); 可替换目标向量的内容。使用 destination.insert(destination.begin(), source.begin(), source.end()); 可将源范围插入开头。
插入位置必须是指向目标向量的有效迭代器。如果插入导致向量存储空间重新分配,指向其元素的所有迭代器、指针和引用都会失效。如果没有发生重新分配,插入位置及其之后的迭代器和引用仍会失效,而之前的仍然有效。插入后,请重新获取已失效的迭代器和引用。
了解向量复制会复制哪些内容
std::vector 会连续存储元素,并拥有这些元素的存储空间。复制向量会创建一个独立的向量,其中包含原向量元素的副本。对于 std::vector<int>,复制的是整数值,因此之后给一个向量中的元素赋值,不会改变另一个向量中的对应值。
其他元素类型的复制行为取决于该类型自身的复制操作。例如,复制一个包含原始指针的向量时,复制的是指针值,不会创建新的指向对象。如果元素是智能指针,则复制行为遵循智能指针自身的语义,例如 std::shared_ptr 会共享所有权。因此,向量彼此独立,并不意味着其元素所指向的对象也彼此独立。
如何选择向量复制方法
要创建新向量,可选择复制构造或范围构造。要替换现有目标向量的内容,可选择复制赋值或 assign。要在指定位置添加一个范围,可选择 insert;要追加元素并让目标向量增长,可将 std::copy 与 std::back_inserter 配合使用。如果要写入已分配好元素空间的目标范围,可使用 std::copy 和大小足够的目标向量。当复制过程还需要对每个元素执行自定义操作时,使用循环和 push_back 很有用。
要完整复制到一个新向量,优先使用复制构造函数;若只复制指定元素,则使用范围构造函数。对于现有向量,可选择赋值或 assign 来替换内容,使用 insert 在指定位置插入元素,或使用 std::back_inserter 追加元素。