顺序型容器使用中一些注意的地方
vector的push_back与emplace_back
- 相对于
push_back
,emplace_back
在添加元素时不会进行额外的复制或者拷贝工作 - 参考链接:http://en.cppreference.com/w/cpp/container/vector/emplace_back
- 上面的特点是
左值引用(&)
与右值引用(&&)
造成的。C++11中引入的右值引用,主要有2个优点(https://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/):
- 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。
- 能够更简洁明确地定义泛型函数。
代码
class A { public: A(int val, int type) : val_(val), type_(type) { cout << "I am being constructed." << endl; } A(A&& other) : val_(other.val_), type_(other.type_) { cout << "I am being moved.\n"; } private: int val_; int type_; }; void test() { cout << "push_back :" << endl; vector<A> vec1; vec1.push_back(A(1, 2)); cout << "emplace_back :" << endl; vector<A> vec2; vec2.emplace_back(1, 2); }
forward_list删除符合条件的节点
forward_list
(单向链表)中可以得到一个节点及其前驱节点,erase_after
用于删除这个节点之后的节点,因此我们需要记录当前节点的前驱节点- 删除特定值时,其前驱节点的信息也会发生改变(iter指向下一个节点)
代码:删除列表中所有的奇数
void test() { forward_list<int> lst; for (int i = 0; i < 10;i++) lst.push_front(i); auto curr = lst.begin(); auto prev = lst.before_begin(); while (curr != lst.end()) { if (*curr % 2 == 1) { curr = lst.erase_after( prev ); } else { prev = curr; ++curr; } } for (auto it = lst.begin(); it != lst.end(); it++) cout << *it << endl; }
迭代器
- 注意:在使用迭代器的时候,尽量不要对容器进行操作,除非十分清楚迭代器的动作或者操作,否则容器操作可能会使得迭代器变得无效。
泛型算法
概述
- 泛型算法本身不会执行容器的操作,它们只会运行于迭代器之上,执行迭代器的操作。因此:泛型算法永远不会改变底层容器的大小。但是一些特殊的迭代器(inserter)会改变底层容器。
一些常用的泛型算法
accumulate
accumulate(begin, end, val)
:第三个参数是和的初值,他的类型决定了函数中使用哪个加法运算符以及返回值的类型(因此必须要求第三个参数的类型实现了加法运算符操作)。code
void test() { vector<int> nums = {1,2,3,4}; cout << accumulate( nums.begin(), nums.end(), 0 ) << endl; vector<string> strs = {"123", "456", "789"}; //cout << accumulate(strs.begin(), strs.end(), "") << endl; // 报错,因为const char* 没有提供加法操作 cout << accumulate(strs.begin(), strs.end(), string("")) << endl; }
fill
- 将给定范围的迭代器内的内容填充为给定数据
code
void test() { vector<int> nums = {1,2,3,4}; fill(nums.begin(), nums.end(), 0); for (auto &n : nums) cout << n << endl; fill_n( nums.begin(), 2, 4 ); // 要保证填充的个数小于容器的size for (auto &n : nums) cout << n << endl; }
replace
,replace_copy
- replace会修改原始数组,而replace_copy不会修改原始数组,会将调整后的数组保存在另外一个地方
code
void test() { vector<int> nums = {1,2,3,4, 1,2}; replace( nums.begin(), nums.end(), 1, 23 ); // 替换 cout << "nums : "; for (auto &num : nums) cout << num << " "; cout << endl; vector<int> cpy; replace_copy( nums.cbegin(), nums.cend(), back_inserter(cpy), 23, 44 ); // 不改变原始数组,而是添加到新的列表中去 cout << "nums : "; for (auto &num : nums) cout << num << " "; cout << endl; cout << "cpy : "; for (auto &num : cpy) cout << num << " "; cout << endl; }
unique
- 功能:可以与
erase
结合,去除容器中的连续重复元素 code
// 输出一个迭代器范围内的数据,最后以换行结束 template<typename iter> void printVec(iter &start, iter &end, ostream &os = cout, string &split = string(" ")) { while (start != end) { os << *start << split; ++start; } os << endl; } void test() { vector<int> nums = {1,1,2,3,4,4, 1,2}; //将唯一的元素放在前面,在这之后的元素都需要删除,这个只会删除相邻的重复元素,即不检查不相邻的元素的值是否相等 auto it = unique(nums.begin(), nums.end()); nums.erase( it, nums.end() ); printVec( nums.begin(), nums.end() ); }
lambda表达式
- 一个lambda表达式表示一个可以调用的代码单元,可以将其理解为一个
未命名的内联函数
,对于有些较为短小的代码段可以使用这种方法 - 形式:
[](){}
,[]
中主要是写lambda表达式内部需要使用的变量()
,被称为捕获,如果是采用引用的方式,则是引用捕获;主要是传参;{}
是函数功能实现部分。 code
template<typename iter> void printVec(iter &start, iter &end, ostream &os = cout, string &split = string(" ")) { while (start != end) { os << *start << split; ++start; } os << endl; } void test() { vector<int> nums = {1,1,2,3,4,4, 1,2}; auto f = [](){return 42; }; cout << f() << endl; std::sort(nums.begin(), nums.end(), [](const int& a, const int& b){return a > b; }); printVec( nums.begin(), nums.end() ); int base = 3; auto it = find_if(nums.begin(), nums.end(), [base](const int& a){ return a < base; }); // 返回第一个满足条件的元素所在的迭代器位置 printVec( it, nums.end() ); }
for_each
对于容器中的每个元素,都进行特定的操作
code
void test() { vector<int> nums = {1,1,2,3,4,4, 1,2}; for_each(nums.begin(), nums.end(), [](const int& a){cout << a*5 << " "; }); cout << endl; }
transform
- 按照某种方法对元素进行变换,需要给定转换后存储的位置
code
void test() { vector<int> nums = {1,1,2,3,4,4, -5,-6}; vector<int> result; transform(nums.begin(), nums.end(), back_inserter(result), [](int a) {return a < 0 ? -a : a; }); transform(nums.begin(), nums.end(), nums.begin(), [](int a) -> int {return a < 0 ? -a : a; }); // ->int是指定返回值类型 for_each(result.begin(), result.end(), [](const int& a){cout << a << " "; }); cout << endl; for_each(nums.begin(), nums.end(), [](const int& a){cout << a << " "; }); cout << endl; }
参数绑定bind
- bind:用占位符绑定数据,可以实现参数位置替换或者参数简化的功能
code
void test() { vector<int> nums = {1,1,2,3,4,4, -5,-6}; vector<int> result; std::sort( nums.begin(), nums.end(), less<int>() ); std::for_each(nums.begin(), nums.end(), [](const int& a){std::cout << a << " "; }); std::cout << endl; // 使用bind函数与占位符,将less中第二个参数与第一个参数位置对调,实现从大到小排序的效果。 // 也可以直接绑定第几个参数为定值等,实现少于原始函数参数个数的运算 std::sort(nums.begin(), nums.end(), bind(less<int>(), std::placeholders::_2, placeholders::_1)); std::for_each(nums.begin(), nums.end(), [](const int& a){std::cout << a << " "; }); std::cout << endl; // 固定第二个参数为3,表示找到一个小于3的数,并记录当前位置 auto it = find_if(nums.begin(), nums.end(), bind(less<int>(), std::placeholders::_1, 3) ); std::for_each(it, nums.end(), [](const int& a){std::cout << a << " "; }); std::cout << endl; }
- 注意事项:如果出现
IntelliSense: "for_each" is ambiguous
的问题,可以在函数前加上命名空间std::
。
back_inserter
,front_inserter
,inserter
back_inserter
是创建一个使用push_back
的迭代器front_inserter
是创建一个使用push_front
的迭代器- ‘inserter’:接受2个参数,分别是容器与迭代器的一个位置iter,所有的元素都会被插入到这个iter之前(依次)
输入输出流迭代器
- 输入输出流迭代器是可以从流中读取数据或者将数据写入流中
代码
void test() { vector<int> nums; vector<int> result; // 输入流迭代器 istream_iterator<int> in_iter(cin); istream_iterator<int> eof; // istream尾部迭代器 while (in_iter != eof) // ctrl+z结束输入 { nums.push_back( *in_iter++ ); } // 输出流迭代器 ostream_iterator<int> os_iter(cout, " "); for (auto &e : nums) *os_iter++ = e; cout << endl; copy( nums.begin(), nums.end(), os_iter ); cout << endl; }
一些泛型算法的特殊情况(list与forward)
list
与forward
都是链表类型,无法做到随机访问迭代容器,因此在对这两种容器进行操作时(sort,remove,reverse,unique
等),优先使用其成员函数的算法,而非泛型算法