1、线性容器

std::array与std::vector不同的是,array对象的大小是固定的,如果容器大小是固定的,那么可以优先考虑使用std::array容器。

由于std::vector是自动扩容的,当存入大量的数据后,并且对容器进行了删除操作,容器并不会自动归还被删除元素相应的内存,这时候需要手动运行shrink_to_fit()释放这部分内存。

std::array C风格接口传参:

void foo(int *p, int len){
return;
}
std::array<int, 4> arr = {1,2,3,4};
//foo(arr,arr.size()); //非法,无法隐式转换
foo(&arr[0], arr.size());
foo(arr.data(), arr.size()); //使用std::sort
std::sort(arr.begin(), arr.end());
//升序
std::sort(arr.begin(), arr.end(), [](int a, int b){
return b > a;
})

std::forward_list是一个列表容器,使用方法和std::list基本类似。和list的双向链表的实现不同,forward_list使用单向链表进行实现,提供了O(1)复杂度的元素插入,不支持快速随机访问,也是标准库容器中唯一一个不提供size()方法的容器。当不需要双向迭代时,具有比list更高的空间利用率。

2、无序容器

传统c++中的有序容器 std::map / std::set,这些元素内部通过红黑树进行实现,插入和搜索的平均复杂度均为O(log(size))。在插入元素时,会根据<操作符比较元素大小并判断元素是否相同,并选择合适的位置插入到容器中。当对这个容器中的元素进行遍历时,输出结果会按照<操作符的顺序来逐个遍历。

而无序容器中的元素是不进行排序的,内部通过Hash表实现,插入和搜索元素的平均复杂度为O(constant),在不关心容器内部元素顺序时,能够获得显著的性能提升。

c++11引入了两组无序容器:std::unordered_map / std::unordered_multimap和std::unordered_set / std::unordered_multiset。

它们的用法和原有的std::map / std::multimap / std::set / std::multiset基本类似。

#include <iostream>
#include <string>
#include <unordered_map>
#include <map> using namespace std; int main(){
unordered_map<int, string> u = {
{1, "1"},
{3, "3"},
{2, "2"}
}; map<int, string> v = {
{1, "1"},
{3, "3"},
{2, "2"}
}; cout << "std::unordered_map" << endl;
for(const auto &n : u){
cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n";
} cout <<endl;
cout << "std::map" << endl;
for (const auto & n : v){
cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n";
}
}

3、元组

传统c++中的容器,除了std::pair外,似乎没有现成的结构能够用来存放不同类型的数据。但std::pair的缺陷是显而易见的,只能保存两个元素。

元组基本操作

三个核心函数:

1、std::make_tuple: 构造元组

2、std::get:获得元组某个位置的值

3、std::tie:元组拆包

#include <iostream>
#include <tuple> using namespace std; auto get_student(int id){
switch (id)
{
case 0:
return make_tuple(3.8, 'A', "张三");
break;
case 1:
return make_tuple(2.9, 'C', "李四");
break;
case 2:
return make_tuple(1.7, 'D', "王五");
break;
default:
return make_tuple(0.0, 'D', "null");
break;
}
} int main(){
auto student = get_student(0);
std::cout << "ID: 0, "
<< "GPA: " << get<0>(student) << ", "
<< "成绩:" << get<1>(student) << ", "
<< "姓名:" << get<2>(student) << "\n"; double gpa;
char grade;
string name; //元祖进行拆包
tie(gpa, grade, name) = get_student(1);
std::cout << "ID: 1, "
<< "GPA: " << gpa << ", "
<< "成绩:" << grade << ", "
<< "姓名:" << name << "\n"; return 0;
}

std::get除了使用常量获取元组对象外,c++14增加了使用类型来获取元组中的对象:

std::tuple<std::string, double, double, int> t("123", 4.5, 6.7, 8);
std::cout << std::get<std::string>(t) << std::endl;
std::cout << std::get<double>(t) << std::endl; //非法,引发编译期错误
std::cout << std::get<int>(t) << std::endl;

运行期索引

std::get<>依赖一个编译期的常量,所以下面的方式是不合法的:

int index = 1;
std::get<index>(t); //非法

c++17引入了std::variant<>,提供给variant<>的类型模版参数 可以让一个variant<>从而容纳提供的几种类型的变量(在其他语言,例如Python/JavaScrpit等,表现为动态类型):

#include <variant>

template <size_t n, typename... T>
constexpr std::variant<T...> _tuple_index(const std::tuple<T...>& tpl, size_t i){
if constexpr(n >= sizeof...(T))
throw std::out_of_range("越界.");
if(i == n)
return std::variant<T...>{
std::in_place_index<n>, std::get<n>(tpl)
}; return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(tpl, i);
}
template <typename... T>
constexpr std::variant<T...> tuple_index(const std::tuple<T...>& tpl, size_t i){
return _tuple_index<0>(tpl, i);
}
template <typename T0, typename ... TS>
std::ostream & operator<< (std::ostream & s, std::variant<T0, TS...> const & v){
std::visit([&](auto && x){s<<x;}, v);
return s;
}

这样我们就能:

int i = 1;
std::cout << tuple_index(student, i) << endl;

元组合并与遍历

还有一个常见的需求就是合并两个元组,这可以通过std::tuple_cat来实现:

auto new_tuple = std::tuple_cat(get_student(1), std::move(t));

要遍历首先我们需要知道一个元组的长度,可以:

template <typename T>
auto tuple_len(T &tpl){
return std::tuple_size<T>::value;
}

这样就能够对元组进行迭代了:

//迭代
for(int i = 0; i != tuple_len(new_tuple); ++i){
//运行期索引
std::cout << tuple_index<i, new_tuple) << std::endl;
}

总结

std::tuple虽然有效,但是标准库提供的功能有限,没办法满足运行期索引和迭代的需求,好在我们还有其他办法可以自行实现。

现代C++教程:高速上手(四)-容器的更多相关文章

  1. 【OpenCV新手教程之十四】OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26977557 作者:毛星云(浅墨) ...

  2. 【OpenCV入门教程之十四】OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑

    http://blog.csdn.net/poem_qianmo/article/details/26977557 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog ...

  3. SpringBoot进阶教程(六十四)注解大全

    在Spring1.x时代,还没出现注解,需要大量xml配置文件并在内部编写大量bean标签.Java5推出新特性annotation,为spring的更新奠定了基础.从Spring 2.X开始spri ...

  4. Senparc.Weixin.MP SDK 微信公众平台开发教程(十四):请求消息去重

    为了确保信息请求消息的到达率,微信服务器在没有及时收到响应消息(ResponseMessage)的情况下,会多次发送同一条请求消息(RequestMessage),包括MsgId等在内的所有文本内容都 ...

  5. 《ArcGIS Engine+C#实例开发教程》第四讲 状态栏信息的添加与实现

    原文:<ArcGIS Engine+C#实例开发教程>第四讲 状态栏信息的添加与实现 摘要:在上一讲中,我们完成了 MapControl 和PageLayoutControl两种视图的同步 ...

  6. iOS 11开发教程(十四)iOS11应用代码添加视图

    iOS 11开发教程(十四)iOS11应用代码添加视图 如果开发者想要使用代码为主视图添加视图,该怎么办呢.以下将为开发者解决这一问题.要使用代码为主视图添加视图需要实现3个步骤. (1)实例化视图对 ...

  7. struts2官方 中文教程 系列十四:主题Theme

    介绍 当您使用一个Struts 2标签时,例如 <s:select ..../>  在您的web页面中,Struts 2框架会生成HTML,它会显示外观并控制select控件的布局.样式和 ...

  8. Linux Shell系列教程之(四)Shell注释

    本文是Linux Shell系列教程的第(四)篇,更多shell教程请看:Linux Shell系列教程 与许多的编程语言一样,Shell中也有注释符号,今天就为大家来介绍下Shell中的注释的语法及 ...

  9. 《Visual C++ 2010入门教程》系列四:VC2010中初学者常见错误、警告和问题

    <Visual C++ 2010入门教程>系列四:VC2010中初学者常见错误.警告和问题   这一章将帮助大家解释一些常见的错误.警告和问题,帮助大家去理解和解决一些常见问题,并了解它的 ...

随机推荐

  1. Android 在代码中修改TextView的DrawableRight等方向上的图片

    在XML文件中可以对TextView进行设置: android:drawableTop="@drawable/XXX" android:drawableBottom="@ ...

  2. unity探索者之复制内容到剪贴板

    版权声明:本文为原创文章,转载请声明http://www.cnblogs.com/unityExplorer/p/8417014.html unity中提供的TextEditor类,只能在window ...

  3. mysql查看死锁及解锁方法

    解除正在死锁的状态有两种方法: 第一种: 1.查询是否锁表 show OPEN TABLES where In_use > 0; 2.查询进程(如果您有SUPER权限,您可以看到所有线程.否则, ...

  4. 随机陷阱和P值

    如果让大家写一个50次的抛硬币实验的可能结果(头像H或字T),多半人在连续三个一样的后,会换一下.因为大家都知道,连续一样的越多,概率越小,越不可能发生.大部分人不会去想,其实HHHTT和HHHHH发 ...

  5. 提升布局能力!理解 CSS 的多种背景及使用场景和技巧

    CSS background是最常用的CSS属性之一.然而,并不是所有开发人员都知道使用多种背景.这段时间都在关注使用多种背景场景.在本文中,会详细介绍background-image`属性,并结合图 ...

  6. 第七篇Scrum冲刺博客--Interesting-Corps

    第七篇Scrum冲刺博客 站立式会议 1.会议照片 2.队友完成情况 团队成员 昨日完成 今日计划 鲍鱼铭 各界面数据请求云函数设计及实现 代码交接及整体架构搭建 叶学涛 进行代码优化 和队友进行交接 ...

  7. SQL语句组合查询 UNION

    1.使用UNION UNION 可以涉及编写多条SELECT语句,首先看看单条语句 第一条SELECT语句把Illinois,Indiana,Michigan等州的缩写传递给IN子句,检索出这些州的所 ...

  8. 牛客网PAT练兵场-跟奥巴马一起编程

    题目地址: 题意:无 /** * *作者:Ycute *时间:2019-11-14-21.29.07 *题目题意简单描述:模拟题输出 */ #include<iostream> #incl ...

  9. 关于Junit4 和 Junit5.4

    1.    Junit5.4 主要是用于Maven框架 , 对普通类的测试是不可以的. 如这个, junit4可以, junit5.4不可以. 2. Junit不可以使用 static 静态方法. 关 ...

  10. 由浅入深理解 IOC 和 DI

    目录 由浅入深理解 IOC 和 DI 开闭原则 OCP(Open Closed Principle) 面向抽象编程 逐步理解实现 IOC 和 DI 的过程(LOL Demo 示例) 比较尴尬的编写程序 ...