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 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
#include
#include
#include
3、元组
传统c++中的容器,除了std::pair外,似乎没有现成的结构能够用来存放不同类型的数据。但std::pair的缺陷是显而易见的,只能保存两个元素。
元组基本操作
三个核心函数:
1、std::make_tuple: 构造元组
2、std::get:获得元组某个位置的值
3、std::tie:元组拆包
#include
#include
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 t("123", 4.5, 6.7, 8);
std::cout << std::get(t) << std::endl;
std::cout << std::get(t) << std::endl; //非法,引发编译期错误
std::cout << std::get(t) << std::endl;
运行期索引
std::get<>依赖一个编译期的常量,所以下面的方式是不合法的:
int index = 1;
std::get(t); //非法
c++17引入了std::variant<>,提供给variant<>的类型模版参数 可以让一个variant<>从而容纳提供的几种类型的变量(在其他语言,例如Python/JavaScrpit等,表现为动态类型):
#include
template
constexpr std::variant _tuple_index(const std::tuple& tpl, size_t i){
if constexpr(n >= sizeof...(T))
throw std::out_of_range("越界.");
if(i == n)
return std::variant{
std::in_place_index, std::get(tpl)
};
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(tpl, i);
}
template
constexpr std::variant tuple_index(const std::tuple& tpl, size_t i){
return _tuple_index<0>(tpl, i);
}
template
std::ostream & operator<< (std::ostream & s, std::variant const & v){
std::visit([&](auto && x){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
auto tuple_len(T &tpl){
return std::tuple_size::value;
}
这样就能够对元组进行迭代了:
//迭代
for(int i = 0; i != tuple_len(new_tuple); ++i){
//运行期索引
std::cout << tuple_index
总结
std::tuple虽然有效,但是标准库提供的功能有限,没办法满足运行期索引和迭代的需求,好在我们还有其他办法可以自行实现。