本节我们将详细介绍移动迭代器适配器,又称之为“移动迭代器”。那么什么是移动迭代器呢?简言之,“*it”表示迭代器解引用,为左值引用,能够访问和二次赋值对象;而使用移动迭代器后,“*it”表示右值引用,直接更改元素的内部指针,实现资源转移。移动迭代器的强大之处在于高效率“移动”对象,但是需要谨慎使用,因为移动后“源对象”处于非定义状态,可能会造成程序的崩溃。
为了更加详细地介绍移动迭代器,我们可以举个例子:现在有两个vector容器,“src”和“dest”,把前者内存储的元素移动到后者,我们会这样做:
#include<iostream>
#include<iterator>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
/*对于基本数据类型比如int、double来说其移动效率与普通copy()相差无几,只有复杂对象才能看到明显效率差距*/
struct dotcpp_user
{
string name;//姓名
int age;//年龄
int grade;//做题个数
bool is_online;//是否在线
};
void test()
{
/*绍移动迭代器*/
/*move_iterator*/
/*创建复杂对象*/
dotcpp_user u1{"user01",18,100,true};
dotcpp_user u2{"user02",18,100,true};
dotcpp_user u3{"user03",18,100,true};
dotcpp_user u4{"user04",18,100,true};
dotcpp_user u5{"user05",18,100,true};
vector<dotcpp_user> src{u1,u2,u3,u4,u5};
vector<dotcpp_user> dest;
dest.resize(src.size());//使用copy需要提前预存空间
copy(src.begin(),src.end(),dest.begin());
for(auto it = dest.begin();it!=dest.end();++it)
{
cout <<it->name << "做了"<< it->grade << "个题目," << "现在" << (it->is_online?"在":"不在") << "线!\n";
}
}
int main(){
system("title dotcpp.com");
test();
return 0;
}编译结果如下:

通过使用copy()算法,进行dest容器向src容器的复制,本质上是dest.push_back()的作用。(注意,此时src容器依然存在)
如果我们使用move_iterator移动迭代器移动呢?
#include<iostream>
#include<iterator>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
/*对于基本数据类型比如int、double来说其移动效率与普通copy()相差无几,只有复杂对象才能看到明显效率差距*/
struct dotcpp_user
{
string name;//姓名
int age;//年龄
int grade;//做题个数
bool is_online;//是否在线
};
void test()
{
/*绍移动迭代器*/
/*move_iterator*/
/*创建复杂对象*/
dotcpp_user u1{"user01",18,100,true};
dotcpp_user u2{"user02",18,100,true};
dotcpp_user u3{"user03",18,100,true};
dotcpp_user u4{"user04",18,100,true};
dotcpp_user u5{"user05",18,100,true};
vector<dotcpp_user> src{u1,u2,u3,u4,u5};
vector<dotcpp_user> dest;
/*构造移动函数*/
move_iterator<vector<dotcpp_user>::iterator>beg(src.begin()) ;
move_iterator<vector<dotcpp_user>::iterator>end(src.end()) ;
copy(beg,end,back_inserter(dest));
for(auto it = dest.begin();it!=dest.end();++it)
{
cout <<it->name << "做了"<< it->grade << "个题目," << "现在" << (it->is_online?"在":"不在") << "线!\n";
}
cout << "由于使用移动构造,直接给源容器给“掏空了”,所以原容器的元素总数没有改变,/n但是对象内部指针所指的对象消失了:\n" ;
cout << "源容器元素个数【" << src.size() << "】,其容量为【" <<src.capacity() << "】\n" ;
cout << "尝试遍历源容器\n";
for(auto it = src.begin();it!=src.end();++it)
{
cout <<it->name << "做了"<< it->grade << "个题目," << "现在" << (it->is_online?"在":"不在") << "线!\n";
}
}
int main(){
system("title dotcpp.com");
test();
return 0;
}编译结果如下:

我们发现,名字不见了!为什么,原因在于移动构造把对象“存”在堆上的资源拿走了!移动构造只是高效转移了资源,而不是创建资源,这就能解释为什么只有复杂对象才能体现移动迭代器的高效性。
上面关于移动构造的代码,移动迭代器和其他迭代器适配器一样,我们也可以使用适配函数进行简化代码的操作:
move_iterator<vector<dotcpp_user>::iterator>beg(src.begin()) ; move_iterator<vector<dotcpp_user>::iterator>end(src.end()) ; copy(beg,end,back_inserter(dest));
可以通过auto自动推导+适配函数(make_move_iterator<类型>())的方式简化构造代码:
auto beg = make_move_iterator(src.begin()) ; auto end = make_move_iterator(src.end()); copy(beg,end,back_inserter(dest));
总结:如果需要高效移动容器内的复杂元素,使用移动迭代器要比普通复制函数高效得多,究其原因在于移动迭代采取右值引用,可理解:为放空源对象指向堆区的指针,该资源让新对象指,源对象指针置空,实现“移动”资源的目的。
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程