UDN-企业互联网技术人气社区

板块导航

浏览  : 1429
回复  : 0

[资源分享] C++11新特性之容器相关特性

[复制链接]
呵呵燕的头像 楼主
发表于 2016-9-18 10:33:15 | 显示全部楼层 |阅读模式
  这是C++11新特性介绍的第四部分,涉及到C++11这次更新中与容器有关的新特性。

  不想看toy code的读者可以直接拉到文章最后看这部分的总结。

  cbegin和cend

  原来的begin和end返回的iterator是否是常量取决于对应的容器类型,但是有时,即使容器不是常量类型,我们也希望获得一个const_iterator,以避免不必要的修改行为。C++11新标准中提供了cbegin和cend函数,无论容器类型,都固定返回const_iterator。

  1.  vector<int> c1 = {0, 1, 2, 3, 4};
  2. auto it1_1 = c1.begin();
  3. auto it1_2 = c1.cbegin();
  4. *it1_1 = 4;
  5. //*it1_2 = 5; // wrong, const iterator's value can't be changed via this iterator.
  6. cout<<*it1_1<<'\t'<<*it1_2<<endl;
复制代码


  标准库的begin和end

  C++11新标准提供了begin和end函数,可以对普通数组使用,获得头指针和尾指针。

  1.  int c2[] = {0, 1, 2, 3, 4};
  2. auto it2_1 = begin(c2);
  3. auto it2_2 = end(c2);
  4. while(it2_1 != it2_2)
  5. {
  6.         cout<<*it2_1<<'\t';
  7.         it2_1++;
  8. }
  9. cout<<endl;
复制代码

  新的赋值方式

  C++11允许使用一个{}包围的初始值列表来进行复制。如果等号左侧是个容器,那么怎么赋值由容器决定。

  1.   vector<int> c3;
  2. c3 = {0, 1, 2, 3, 4};
  3. for(auto it3_1 = c3.begin(); it3_1 != c3.end(); it3_1++)
  4.         cout<<*it3_1<<'\t';
  5. cout<<endl;
复制代码

  initializer_list

  C++11新标准中新增了initializer_list类型,其实在之前介绍初始化的那篇文章中,使用vector v = {0, 1, 2, 3, 4}这种初始化形式时,就隐式的使用了initializer_list:每当在程序中出现一段以{}包围的字面量时,就会自动构造一个initializer_list对象。

  另外,initializer_list的另一个作用就在于作为函数的形参,这样的函数可以方便的传入以{}包围的不定长列表:

  1. void print_list(initializer_list<int> il)
  2. {
  3.         for(auto it = il.begin(); it != il.end(); it++)
  4.         {
  5.                 cout<<*it<<'\t';
  6.                 //*it = 100; // wrong. initializer_list element is read-only.
  7.         }
  8.         cout<<endl;
  9. }

  10. print_list({0, 1, 2, 3, 4});
  11. print_list({0, 1, 2, 3, 4, 5});
复制代码


  但是,需要注意的是,initializer_list中的元素是只读的。

  array

  C++11标准中提供了定长数组容器array,相比于普通数组更安全、更易使用。array是定长数组,所以不支持诸如插入、删除等改变容器大小的操作,但是可以对元素进行赋值改变其值。

  1.  array<int, 5> c4 = {0, 1, 2, 3, 4};
  2. c4[3] = 100; // can't insert since the array size is fixed.
  3. for(auto it4_1 = c4.begin(); it4_1 != c4.end(); it4_1++)
  4. {
  5.         cout<<*it4_1<<'\t';
  6. }
  7. cout<<endl;
复制代码


  forward_list

  C++11标准中增加了新的容器forward_list,提供了一个快速的、安全的单向链表实现。因为是单向链表,所以也就没有rbegin、rend一类的函数支持了。

  同样是因为单向链表的缘故,无法访问到给定元素的前驱,所以没有提供insert函数,而对应提供了一个insert_after函数,用于在给定元素之后插入节点。erase_after、emplace_after同理。

  1.  forward_list<int> c5 = {3, 4};
  2. c5.push_front(2);
  3. c5.push_front(1);
  4. auto it5_1 = c5.before_begin();
  5. c5.insert_after(it5_1, 0);
  6. for(auto it5_2 = c5.begin(); it5_2 != c5.end(); it5_2++)
  7. {
  8.         cout<<*it5_2<<'\t';
  9. }
  10. cout<<endl;
复制代码


  swap

  新标准中提供了非成员版本的swap操作,此操作对array容器,会交换元素的值;对其他容器,则只交换容器的内部结构,并不进行元素值的拷贝操作,所以在这种情况下是非常迅速的。

  正因如此,当swap array后,原来array上的迭代器还依然指向原有元素,只是元素的值变了;

  而swap非array容器之后,原来容器上的迭代器将指向对方容器上的元素,而指向的元素的值却保持不变。

  1. vector<int> c6 = {0, 1, 2, 3, 4};
  2. vector<int> c7 = {5, 6, 7, 8, 9};
  3. auto it6_1 = c6.begin();
  4. auto it7_1 = c7.begin();
  5. swap(c6, c7);
  6. for(auto it6_2 = c6.begin(); it6_2 != c6.end(); it6_2++)
  7.         cout<<*it6_2<<'\t';
  8. cout<<endl;
  9.        
  10. for(auto it7_2 = c7.begin(); it7_2 != c7.end(); it7_2++)
  11.         cout<<*it7_2<<'\t';
  12. cout<<endl;

  13. cout<<(it6_1 == c7.begin())<<'\t'<<(it7_1 == c6.begin())<<endl;

  14. array<int, 5> c8 = {0, 1, 2, 3, 4};
  15. array<int, 5> c9 = {5, 6, 7, 8, 9};
  16. auto it8_1 = c8.begin();
  17. auto it9_1 = c9.begin();
  18. swap(c8, c9);
  19. cout<<(it8_1 == c8.begin())<<'\t'<<(it9_1 == c9.begin())<<endl;
复制代码


  emplace

  emplace操作将使用接受的参数构造一个对应容器中的元素,并插入容器中。这一点,使用普通的insert、push操作是做不到的。

  class TestData
  1. {
  2.         public:
  3.                 TestData(string name, int age, double salary): name(name), age(age), salary(salary)
  4.                 {}
  5.         private:
  6.                 string name;
  7.                 int age;
  8.                 double salary;
  9. };

  10. vector<TestData> c10;
  11. c10.emplace_back("yubo", 26, 100000000000.0);
  12. //c10.push_back("laowang", 56, 10.5); // wrong. no 3 params push_back
  13. c10.push_back(TestData("laowang", 56, 10.5));
  14. cout<<c10.size()<<endl;
复制代码


  shrink_to_fit

  一般可变长容器会预先多分配一部分内存出来,以备在后续增加元素时,不用每次都申请内存。所以有size和capacity之分。size是当前容器中存有元素的个数,而capacity则是在不重新申请内存的情况下,当前可存放元素的最大数目。而shrink_to_fit就表示将capacity中的多余部分退回,使其回到size大小。但是,这个函数的具体效果要依赖于编译器的实现……

  1. vector<int> c11;
  2. for(int i = 0; i < 24; i++)
  3.         c11.push_back(i);
  4. cout<<c11.size()<<'\t'<<c11.capacity()<<endl;
  5. c11.shrink_to_fit();
  6. cout<<c11.size()<<'\t'<<c11.capacity()<<endl;
复制代码


  无序关联容器

  C++11新标准中引入了对map、set等关联容器的无序版本,叫做unorderer_map\/unordered_set。

  无序关联容器不使用键值的比较操作来组织元素顺序,而是使用哈希。这样在某些元素顺序不重要的情况下,效率更高。

  1.   unordered_map<string, int> c12;
  2. map<string, int> c13;
  3. string string_keys[5] = {"aaa", "bbb", "ccc", "ddd", "eee"};
  4. for(int i = 0; i < 5; i++)
  5. {
  6.         c12[string_keys[i]] = i;
  7.         c13[string_keys[i]] = i;
  8. }
  9. cout<<"normal map:\n";
  10. for(auto it13 = c13.begin(); it13 != c13.end(); it13++)
  11.         cout<<it13->first<<':'<<it13->second<<'\t';
  12. cout<<endl;
  13. cout<<"unordered map:\n";
  14. for(auto it12 = c12.begin(); it12 != c12.end(); it12++)
  15.         cout<<it12->first<<':'<<it12->second<<'\t';
  16. cout<<endl;
复制代码


  tuple

  熟悉python的程序员应该对tuple都不陌生,C++11中也引入了这一数据结构,用于方便的将不同类型的值组合起来。

  可以通过如下方式,获取tuple中的元素、tuple的长度等:

  1.   //tuple<int, string, vector<int>> c14 = {1, "tuple", {0, 1, 2, 3, 4}}; // wrong. must explicit initialize
  2. tuple<int, string, vector<int>> c14{1, "tuple", {0, 1, 2, 3, 4}};
  3. get<0>(c14) = 2;
  4. typedef decltype(c14) ctype;
  5. size_t sz = tuple_size<ctype>::value;
  6. cout<<get<0>(c14)<<'\t'<<get<1>(c14)<<'\t'<<get<2>(c14)[0]<<'\t'<<sz<<endl;
复制代码


  总结

  cbegin和cend提供了固定获取const_iterator的方式。

  begin和end用于普通数组获得首尾指针。

  可以使用{}包围的初始值列表进行赋值。

  增加initializer_list类型用于方便的使用{}包围的不定长列表。

  增加新的定长数组容器array 单向链表容器forward_list。

  增加非成员函数版本的swap操作。对array swap只交换元素值,而容器的结构不变;对其他容器则只改变容器数据结构,而元素值不变。

  增加emplace操作用于将参数传递给构造函数构造元素并插入容器。

  增加shrink_to_fit函数用于退回多余的空间。

  增加无序关联容器。

  增加tuple容器。

  完整代码详见container.cpp

原文作者:一根笨茄子 来源:开发者头条

相关帖子

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部