文章目录
C++ 容器 (Container)
基本用法
获得容器的长度
我们在之前常常使用sizeof()
获得数组的长度:
int data[17];
size_t data_size = sizeof(data)/sizeof(data[0]);
printf("Size of array: %zu",data_size);
但是使用容器的size()
方法,可以更方便的获得数据长度:
std::array<int,17> data_{};
std::cout<<"Size of array: "<<data_.size()<<std::endl;
判断容器是否为空
No standard way of checking if empty
int full_arr[5] = {1,2,3,4};
printf("Array empty: %d",full_arr[0]==NULL);
empty()
std::vector<int> empty_vec_{};
std::cout<<"Array empty: "<<empty_vec_.empty()<<std::endl;
获取最后一个数据
如果不使用容器的话,获取最后一个元素可能出界:
float f_arr[N] = {1.5,2.3};
printf("Last element : %f",f_arr[3]);
如今可以直接使用back()
方法
std::array<float,2> f_arr_{1.5,2.3};
cout<<"Last element: "<<f_arr_.back()<<endl;
清除成员
clear()
std::vector<char> letters = {'n','a'};
letters.clear();
当然也可以使用std::string
类型
std::string letters{"nacho"};
letters.clear();
什么情况下使用容器?
这里我们应该反问一句,为什么不适用容器呢?相比于数组,容器不仅让程序更加易读,而且还有更多的方法,例如size()
、empty()
、front()
等等,还支持STL算法。
常见容器
std::array
头文件:#include<array>
创建数组容器:std::array<float,3> var = {1.0f,2.0f,3.0f}
,存放一系列相同数据类型的变量。
例子
#include<iostream>
#include<array>
using std::cout;
using std::endl;
int main(){
std::array<float,3> container_float = {1.1f, 2.2f, 3.3f};
for(const float & element : container_float){
cout<<element<<endl;
}
cout<<std::boolalpha<<endl;
cout<<"Array size: "<< container_float.size()<<endl;
cout<<"Array empty: "<<container_float.empty()<<endl;
return 0;
}
std::vector
这里向量的用法和之前的数组类似,导入头文件#include<vector>
,vector的实现方式是动态表格(Dynamic table),因此能够增添和删减元素:
- 清除所有数据:
clear()
- 增添一个元素,历史上常用
vec.push_back(value)
,但是在C++11中更推荐使用vec.emplace_back(value)
添加元素
值得注意的是,vector并不注重size的说法,我们更关注一个vector预留的内存大小,即capacity,这也是我们优化vector的一个方向,即应预留足够的内存空间,避免频繁的内存申请和释放,使用到的方法就是reserve(n)
,n是预计存放变量的最大数量。
案例:
创建两个vector,第一个vector预先分配了足够的空间,第二个没有分配足够空间,由系统自动分配内存
#include<iostream>
#include<vector>
using std::cout;
using std::endl;
int main(){
const int N = 100;
std::vector<int> v1;
cout<<"v1 has been created\ncapacity:"<<v1.capacity()<<" size:"<<v1.size()<<endl;
v1.reserve(N);
cout<<"After v1.capacity() \ncapacity:"<<v1.capacity()<<" size:"<<v1.size()<<endl;
for(int i=0; i<N ; i++){
v1.emplace_back(i);
}
cout<<"v1 has been filled \ncapacity:"<<v1.capacity()<<" size:"<<v1.size()<<endl;
// v2
std::vector<int> v2;
cout<<"v2 has been created\ncapacity:"<<v2.capacity()<<" size:"<<v2.size()<<endl;
for(int i=0; i<N ; i++){
v2.emplace_back(i);
}
cout<<"v2 has been filled \ncapacity:"<<v2.capacity()<<" size:"<<v2.size()<<endl;
return 0;
}
从结果不难发现:
- vector初始化后的capacity为0
- 如果不提前分配内存,不仅可能会进行频繁的内存申请而浪费时间,也会浪费内存。
应用
Open3D::PointCloud
std::map
映射类型
案例
#include<iostream>
#include<map>
#include<string>
int main(){
std::map<int,std::string> students;
students.emplace(3,"xiaoming"); //[3]
students.emplace(1,"xiaohong"); //[1]
students.emplace(2,"xiaohong"); //[2]
for(const auto& [id,name] : students){
std::cout<<"id:"<<id<<" name:"<<name<<std::endl;
}
std::cout<<"size:"<<students.size()<<std::endl;
return 0;
}
输出结果
可见,这里的键值对被重新排序了。
std::unordered_map
这种方法的排序是通过哈希表实现的,所以键应该是可以被哈希函数处理的,例如整数和字符串等,只用将map
改为unordered_map
即可,上面的案例在无序列表中运行如下所示,按照哈希函数进行随机排列
目前所有的内置类型都已经实现了哈希函数
Iterating over maps
之前的想法中,我们可以使用下面的方法迭代map
for(const auto& kv : m){
const auto& key = kv.first;
const auto& value = kv.second;
// Do work
}
而在C++17中,我们可以简写为以下形式
for(const auto& [key,value] : m){
// Do work
}
Much More about container
C++迭代器 (Iterators)
假设我们当前需要遍历输出vector、array和list的每个元素,以往的做法是针对每种类型创建一个函数,但是使用迭代器可以通过重载更简洁的实现这个功能:
template<typename Iterator>
void print_it(Iterator begin, Iterator end){
for(Iterator it = begin; it!=end; it++){
std::cout<<*it<<" ";
}
std::cout<<std::endl;
}
可见,C++中的迭代器充当了标准库算法和数据结构之间的胶水,最小化了算法对所操作的数据结构的依赖。
STL就是通过使用迭代器获取容器的元素,*iter
指向当前位置的元素,++
指向下一个位置的指针,也可以通过==
、!=
和<
进行比较。
容器也提供了不同的迭代器,例如begin
、end
等。
STL Algorithms
STL有大约80多种算法,通过#include<algorithm>
进行调用,从而避免重复造轮子(Don’t reinvent the wheel.)。
下面是常用的STL算法
-
std::sort(s.begin(),s.end())
,默认升序排列 -
std::find(v.begin(),v.end(),n1)
auto result = std::find(v.begin(),v.end(),n1); if(result!=std::end(v)){ cout<<"successful"; } else{ cout<<"false"; }
-
std::fill(v.begin(),v.end(),n1)
-
std::count(v.begin(),v.end(),n1)
-
std::count_if
,满足条件的计数inline bool div_by_3(int i){return i%3==0;} int main(){ std::vector<int> v{1,2,3,4,4,5,6,6}; int result = std::count_if }
-
std::for_each(begin,end,function_handle)
#include<vector> #include<iostream> #include<algorithm> using std::cout; using std::endl; int main(){ // 使用std::for_each将向量的每个元素输出 std::vector<int> v1{1,2,3,4,5,6}; //匿名函数 auto print = [](const int a){cout<<a<<" "; }; std::for_each(v1.begin(),v1.end(),print); return 0; }
-
std::all_off()
,判断所有成员是否满足条件inline bool even(int i){return i%2==0;}; int main(){ std::vector<int> v{2,4}; bool all_is_even = all_of(v.begin(),v.end(),even); }
-
std::rotate()
,将成员旋转int main () { std :: vector <int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; cout << "before rotate: "; Print(v); std :: rotate(v.begin (), v.begin () + 2, v.end ()); cout << "after rotate: "; Print(v); } //before rotate: 1 2 3 4 5 6 7 8 9 10 //after rotate: 3 4 5 6 7 8 9 10 1
-
std::transform()
,将一个容器的成员逐个转化为另外一个容器的成员#include<iostream> #include<string> #include<algorithm> using std::cout; using std::endl; auto UpperCase(char c){return std::toupper(c);} int main(){ const std::string s("hello"); std::string S(s); std::transform(s.begin(),s.end(),S.begin(),UpperCase); cout<<s<<endl; cout<<S<<endl; return 0; } // 输出 // hello // HELLO
-
std::accumulate()
,可实现累加、累乘等操作int main () { std :: vector <int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int sum = std :: accumulate (v.begin (), v.end (), 0); int product = std :: accumulate (v.begin (), v.end (), 1, std :: multiplies ()); cout << "Sum : " << sum << endl; cout << "Product: " << product << endl; } // Sum : 55 // Product: 3628800
-
std::min_element()
int main () { std :: vector <int> v{3, 1, 4, 1, 0, 5, 9}; auto result = std :: min_element (v.begin (), v.end ()); auto min_location = std :: distance(v.begin (), result); cout << "min at: " << min_location << endl; } // min at: 4
-
std::minmax_element()
int main () { using std :: minmax_element ; auto v = {3, 9, 1, 4, 2, 5, 9}; auto [min , max] = minmax_element (begin(v), end(v)); cout << "min = " << *min << endl; cout << "max = " << *max << endl; } // min = 1 // max = 9
-
std::clamp()
int main () { // value should be between [kMin ,kMax] const double kMax = 1.0F; const double kMin = 0.0F; cout << std :: clamp (0.5 , kMin , kMax) << endl; cout << std :: clamp (1.1 , kMin , kMax) << endl; cout << std :: clamp (0.1 , kMin , kMax) << endl; cout << std :: clamp (-2.1, kMin , kMax) << endl; } // 0.5 // 1 // 0.1 // 0
-
std::sample()
,取样int main () { std :: string in = "C++ is cool", out; auto rnd_dev = std :: mt19937{ random_device {}() }; const int kNLetters = 5; std :: sample(in.begin (), in.end (), std :: back_inserter (out), kNLetters , rnd_dev); cout << "from : " << in << endl; cout << "sample: " << out << endl; } // from : C++ is cool // sample: C++cl