C++ copy()、copy_backward()和copy_if()的用法(附带实例)
C++ 程序中,我们经常需要将一个容器中的元素复制到另一个容器中,以完成数据的备份或进行其他处理。
为了完成这个任务,我们可能会想到使用 for 循环来访问源容器中的数据,然后调用 push_back() 函数将数据添加到目标容器中,或者直接使用赋值运算符“=”将源容器赋值给目标容器来完成元素的复制。这两种方法都可以完成任务,但都显得有些“粗鲁”:
为了让复制操作更加“优雅”,STL 提供了通用的 copy() 算法来完成容器元素的复制。
这样,copy() 算法会将前两个参数所表示的源容器范围内的数据复制到第三个参数表示的目标容器的开始位置,并依次往后排列。
复制完成后,copy() 算法会返回指向目标容器中被复制元素的结束位置,也就是最后一个元素的下一个位置。
在使用 copy() 算法时,需要注意两点:
下面来看一个实际例子。老师在统计成绩时,通常会将多个班级的学生成绩汇总到一起,形成一张学生成绩总表。通过使用 copy() 算法,整个过程可以变得更加高效和简便:
为了让这个总容器能够容纳所有即将复制进来的成绩,我们使用 resize() 函数将其容量调整为两个分容器的容量之和。接着,以 vecScore 容器的开始位置(vecScore.begin())为起点,利用 copy() 函数将 vecScoreC1 中的所有数据(从 vecScoreC1.begin() 到 vecScoreC1.eng())复制到 vecScore 容器中。
copy() 函数复制完成后,会返回一个目标容器的迭代器,它指向的是所有复制进来的数据的结束位置,而这个位置正好是我们第二次复制的开始位置,所以我们将它保存下来并用作第二次复制的开始位置。
利用两次复制可以成功将两个容器中的数据复制到新容器中,实现数据的连接汇总,如下图所示:

图 1 copy()算法实现的数据连接
在这种情况下,可以使用 copy() 算法的变种—copy_backward() 算法来实现。
copy_backward() 算法的原型如下:
这意味着,copy_backward() 算法会确保被复制的数据在这里结束。此外,copy() 算法返回的是指向被复制元素在目标容器中的结束位置的迭代器,而 copy_backward() 算法返回的迭代器则指向这些元素的开始位置。
以下是一个使用 copy_backward() 算法的 C++ 示例代码:
例如,在学校中的评优活动,我们希望将优秀的成绩筛选出来拿去评优,而不是不加选择地将所有成绩都拿去评优。在这种情况下,就需要使用 STL 中的 copy_if() 算法来实现数据的筛选复制。
与前面的 copy() 算法相似,copy_if() 算法也需要指定源容器中被复制数据的范围以及目标容器中的起始位置。不同的是,copy_if() 算法还需要接受一个函数作为第四个参数,用以表达我们的筛选规则。
规则函数的返回值为 bool 类型,拥有一个与被复制数据类型相同的参数。在进行复制时,copy_if() 算法会逐个将复制范围内的数据传递给这个函数进行判断,只有符合筛选规则(即规则函数返回值为 true)的数据才会被复制。这样,我们就可以将源容器中的符合筛选条件的数据复制到目标容器中,实现数据的筛选复制。
利用 copy_if() 算法,我们可以很轻松地从所有学生成绩中筛选出优秀的成绩来:
为了完成这个任务,我们可能会想到使用 for 循环来访问源容器中的数据,然后调用 push_back() 函数将数据添加到目标容器中,或者直接使用赋值运算符“=”将源容器赋值给目标容器来完成元素的复制。这两种方法都可以完成任务,但都显得有些“粗鲁”:
- 使用 for 循环过于烦琐;
- 使用赋值运算符只能复制整个容器中的所有数据,缺乏灵活性。
为了让复制操作更加“优雅”,STL 提供了通用的 copy() 算法来完成容器元素的复制。
C++ copy()
在 STL 中,copy() 算法的原型如下:template <class InputIterator, class OutputIterator> OutputIterator copy ( InputIterator first, InputIterator last, OutputIterator result );copy() 算法可以接受三个参数,前两个参数表示需要复制的源容器的起始位置和终止位置,它们共同定义了需要复制的数据元素的范围。第三个参数则是目标容器的起始位置。
这样,copy() 算法会将前两个参数所表示的源容器范围内的数据复制到第三个参数表示的目标容器的开始位置,并依次往后排列。
复制完成后,copy() 算法会返回指向目标容器中被复制元素的结束位置,也就是最后一个元素的下一个位置。
在使用 copy() 算法时,需要注意两点:
- 一是确保在目标容器的复制开始位置之后有足够的空间来容纳即将被复制的数据。必要时,可以根据被复制数据的数量,使用容器的 resize() 函数调整目标容器的大小;
- 二是当复制行为发生在同一个容器中时,源范围和目标范围最好不要重叠,否则可能会导致数据混乱。
下面来看一个实际例子。老师在统计成绩时,通常会将多个班级的学生成绩汇总到一起,形成一张学生成绩总表。通过使用 copy() 算法,整个过程可以变得更加高效和简便:
// 保存 C1 和 C2 班级成绩的容器 vector<int> vecScoreC1; vector<int> vecScoreC2; // 对容器进行操作,将各个班级的成绩保存到各自的容器中 // ... // 保存所有成绩的成绩总表容器 vector<int> vecScore; // 根据各个分容器的大小,重新设定总容器的容量 // 使它可以容纳即将复制进来的所有数据 vecScore.resize( vecScoreC1.size() + vecScoreC2.size() ); // 将第一个容器 vecScoreC1 中的数据复制到 vecScore 中 auto lastit = copy(vecScoreC1.begin(), vecScoreC1.end(), vecScore.begin() ); // 复制的目标范围和位置 // 将第二个容器 vecScoreC2 中的数据追加到 vecScore 的末尾 copy(vecScoreC2.begin(), vecScoreC2.end(), lastit ); // 以上一次复制的结束位置作为第二次复制的开始位置在这段代码中,分别使用两个容器来保存两个班级的成绩,同时定义了 vecScore 容器来保存汇总后两个班级的总成绩。
为了让这个总容器能够容纳所有即将复制进来的成绩,我们使用 resize() 函数将其容量调整为两个分容器的容量之和。接着,以 vecScore 容器的开始位置(vecScore.begin())为起点,利用 copy() 函数将 vecScoreC1 中的所有数据(从 vecScoreC1.begin() 到 vecScoreC1.eng())复制到 vecScore 容器中。
copy() 函数复制完成后,会返回一个目标容器的迭代器,它指向的是所有复制进来的数据的结束位置,而这个位置正好是我们第二次复制的开始位置,所以我们将它保存下来并用作第二次复制的开始位置。
利用两次复制可以成功将两个容器中的数据复制到新容器中,实现数据的连接汇总,如下图所示:

图 1 copy()算法实现的数据连接
C++ copy_backward()
使用 copy() 算法复制数据时,我们需要指定被复制数据在目标容器中的开始位置。然而,有时这个开始位置很难确定,而相应的结束位置却很好确定,或者是我们对结束位置有特殊的要求。在这种情况下,可以使用 copy() 算法的变种—copy_backward() 算法来实现。
copy_backward() 算法的原型如下:
template <class BidirectionalIterator1, class BidirectionalIterator2> BidirectionalIterator2 copy_backward ( BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 result );copy_backward() 算法与 copy() 算法的使用非常相似,两者唯一的不同在于第三个参数和返回值的含义。在 copy() 算法中,第三个参数是被复制数据在目标容器中的开始位置,而在 copy_backward() 算法中,第三个参数成了结束位置。
这意味着,copy_backward() 算法会确保被复制的数据在这里结束。此外,copy() 算法返回的是指向被复制元素在目标容器中的结束位置的迭代器,而 copy_backward() 算法返回的迭代器则指向这些元素的开始位置。
以下是一个使用 copy_backward() 算法的 C++ 示例代码:
// 保存学生对象的容器 vector<Student> vecStudent; // 将数据保存到容器中 // 扩大容器的容量为原来的两倍 // 这样容器中前半部分是已有的数据,后半部分是默认生成的数据 vecStudent.resize( vecStudent.size() * 2 ); // 将前半部分已有的数据复制到后半部分,替换掉后半部分默认的数据 copy_backward(vecStudent.begin(), vecStudent.begin() + vecStudent.size() / 2, // 前半部分范围 vecStudent.end() ); // 指定结束位置,复制数据填满容器在这段代码中,首先定义了一个用于保存学生对象的容器,并利用 resize() 函数将容器扩容为原来的两倍。这时,容器的前半部分是已有的数据,而后半部分是默认生成的数据。然后,利用 copy_backward() 算法将容器中前半部分的数据复制到自身容器的后半部分,这样就实现了容器中学生数据的复制。
C++ copy_if()
无论是 copy() 算法还是 copy_backward() 算法,它们对源容器中数据的复制,都是不加选择地全盘复制、照单全收。然而,有时我们需要对复制的数据进行筛选,只复制那些符合某个条件的数据。例如,在学校中的评优活动,我们希望将优秀的成绩筛选出来拿去评优,而不是不加选择地将所有成绩都拿去评优。在这种情况下,就需要使用 STL 中的 copy_if() 算法来实现数据的筛选复制。
与前面的 copy() 算法相似,copy_if() 算法也需要指定源容器中被复制数据的范围以及目标容器中的起始位置。不同的是,copy_if() 算法还需要接受一个函数作为第四个参数,用以表达我们的筛选规则。
规则函数的返回值为 bool 类型,拥有一个与被复制数据类型相同的参数。在进行复制时,copy_if() 算法会逐个将复制范围内的数据传递给这个函数进行判断,只有符合筛选规则(即规则函数返回值为 true)的数据才会被复制。这样,我们就可以将源容器中的符合筛选条件的数据复制到目标容器中,实现数据的筛选复制。
利用 copy_if() 算法,我们可以很轻松地从所有学生成绩中筛选出优秀的成绩来:
// 包含需要的头文件 #include <vector> #include <algorithm> // 为了使用 copy_if() 算法 #include <iostream> using namespace std; // 复制规则函数 bool isgood(int n) { // 优秀的成绩(大于或等于 85)才会返回 true,才会被复制 return n >= 85 ? true : false; } int main() { // 保存所有成绩的源容器 vector<int> vecAll = {64,89,91,68,99,75}; // 用于保存筛选出来的优秀学生成绩的目标容器 vector<int> vecGood; // 为目标容器预留足够的空间 vecGood.resize(vecAll.size()); // 使用 copy_if() 算法将源容器中的优秀成绩复制到目标容器中 // copy_if() 算法的返回值,即为目标容器中被复制数据的末尾位置 auto itend = copy_if(vecAll.begin(), vecAll.end(), // 复制的范围 vecGood.begin(), // 目标容器的起始位置 isgood); // 复制规则 cout<<"筛选出来的优秀成绩: "<<endl; // 使用 for_each() 算法输出复制到目标容器中的数据 for_each(vecGood.begin(), itend, // 使用 copy_if() 算法的返回值作为结束位置 [] (int a) { // 使用 Lambda 表达式输出数据 cout<<a<<endl; }); return 0; }运行结果为:
筛选出来的优秀成绩:
89
91
99
相关文章
- C++ copy_backward(STL copy_backward)算法详解
- C++ copy_n(STL copy_n)算法详解
- C++ copy_if(STL copy_if)算法详解
- C++ reverse_copy(STL reverse_copy)算法详解
- C++ rotate_copy(STL rotate_copy)算法详解
- C++ partition_copy()函数详解
- C#复制字符串(Copy()和CopyTo())
- C++ remove、remove_copy、remove_if和remove_copy_if函数使用详解
- C++ replace,replace_if和replace_copy函数用法详解
- C语言的优点和缺点