modern_cpp_5-C++ STL container&iterator

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;
}

在这里插入图片描述

从结果不难发现:

  1. vector初始化后的capacity为0
  2. 如果不提前分配内存,不仅可能会进行频繁的内存申请而浪费时间,也会浪费内存。

应用

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指向当前位置的元素,++指向下一个位置的指针,也可以通过==!=<进行比较。

容器也提供了不同的迭代器,例如beginend等。

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
    

本节课的Slides链接课程地址

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值