现代C++教程:高速上手(四)-容器
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++教程:高速上手(四)-容器的更多相关文章
- 【OpenCV新手教程之十四】OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26977557 作者:毛星云(浅墨) ...
- 【OpenCV入门教程之十四】OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑
http://blog.csdn.net/poem_qianmo/article/details/26977557 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog ...
- SpringBoot进阶教程(六十四)注解大全
在Spring1.x时代,还没出现注解,需要大量xml配置文件并在内部编写大量bean标签.Java5推出新特性annotation,为spring的更新奠定了基础.从Spring 2.X开始spri ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十四):请求消息去重
为了确保信息请求消息的到达率,微信服务器在没有及时收到响应消息(ResponseMessage)的情况下,会多次发送同一条请求消息(RequestMessage),包括MsgId等在内的所有文本内容都 ...
- 《ArcGIS Engine+C#实例开发教程》第四讲 状态栏信息的添加与实现
原文:<ArcGIS Engine+C#实例开发教程>第四讲 状态栏信息的添加与实现 摘要:在上一讲中,我们完成了 MapControl 和PageLayoutControl两种视图的同步 ...
- iOS 11开发教程(十四)iOS11应用代码添加视图
iOS 11开发教程(十四)iOS11应用代码添加视图 如果开发者想要使用代码为主视图添加视图,该怎么办呢.以下将为开发者解决这一问题.要使用代码为主视图添加视图需要实现3个步骤. (1)实例化视图对 ...
- struts2官方 中文教程 系列十四:主题Theme
介绍 当您使用一个Struts 2标签时,例如 <s:select ..../> 在您的web页面中,Struts 2框架会生成HTML,它会显示外观并控制select控件的布局.样式和 ...
- Linux Shell系列教程之(四)Shell注释
本文是Linux Shell系列教程的第(四)篇,更多shell教程请看:Linux Shell系列教程 与许多的编程语言一样,Shell中也有注释符号,今天就为大家来介绍下Shell中的注释的语法及 ...
- 《Visual C++ 2010入门教程》系列四:VC2010中初学者常见错误、警告和问题
<Visual C++ 2010入门教程>系列四:VC2010中初学者常见错误.警告和问题 这一章将帮助大家解释一些常见的错误.警告和问题,帮助大家去理解和解决一些常见问题,并了解它的 ...
随机推荐
- jq js 获取子元素
js this.children[1].className=""this.firstChild.className = ""this.lastChild.cla ...
- CentOS7 安装图形化桌面
1.装好CentOS7后,我们一开始是上不了网的 (ping 百度报错:Name or service not know) 2.输入dhclient,可以自动获取一个IP地址,再用命令ip addr查 ...
- 如何选择一台适合Java开发的电脑
前言 最近在群里有同学求推荐Java开发用的电脑,所以胖哥就出个简单的专题,用我贫瘠的电脑知识来帮助大家选择适合开发的电脑配置.因为家里的主机已经带不动两个 IDEA 了,更别提开个 Docker 啥 ...
- Ubuntu下载连接(阿里云镜像)
ubuntu 14.04: http://mirrors.aliyun.com/ubuntu-releases/14.04/ ubuntu 16.04: http://mirrors.aliyun.c ...
- 易盛信息9.0外盘期货行情数据API接口公共授权开发包例子代码
易盛信息9.0外盘期货行情数据API接口公共授权开发包例子代码 怎么才能获取到外盘期货行情数据API接口呢?不少朋友就会考虑到易盛9.0行情API接口,本身易盛就是一个软件提供商,提供行 ...
- Python语言中的关键字(自己做的读书笔记)
电脑配置:联想笔记本电脑 windows8系统 Python版本:2.7.8 本文章撰写时间:2015.1.1 作者:陈东陈 阅读说明: 1.本文都是先解释,后放图片: 2.文中斜体部分要么为需要输入 ...
- 6套MSP430开发板资料共享 | 免费下载
目录 1-MSP430 开发板I 2-MSP430mini板资料 3-MSP430F149开发板资料 4-KB-1B光盘资料 5-LT-1B型MSP430学习板光盘 6-MSP-EXP430F552 ...
- Shell编程—sed和gawk
1文本处理 1.1sed 编辑器 sed编辑器被称作流编辑器(stream editor),和普通的交互式文本编辑器恰好相反.在交互式文本编辑器中(比如vim),你可以用键盘命令来交互式地插入.删除或 ...
- Hadoop上小文件如何存储?
Block是文件块,HDFS中是以Block为单位进行文件的管理的,一个文件可能有多个块,每个块默认是3个副本,这些块分别存储在不同机器上.块与文件之前的映射关系会定时上报Namenode.HDFS中 ...
- C#串口通讯,复制粘贴就可用,仅仅介绍怎样最快的搭建一个串口通讯,异常拦截等等需要自己加上
using System; using System.Collections.Generic; using System.IO.Ports; using System.Text; //串口通讯类 pu ...