美文网首页
C++ STL标准模板库入门学习与应用

C++ STL标准模板库入门学习与应用

作者: RobertY | 来源:发表于2017-11-02 16:40 被阅读65次

    C++ 标准模板库的核心包括以下三个组件:

    1. 容器(Containers)
      • deque、list、vector、map等
    2. 算法(Algorithms)
      • 算法作用于容器。它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作
    3. 迭代器(iterators)
      • 迭代器用于遍历对象集合的元素。这些集合可能是容器,也可能是容器的子集

    C++ 11 新特新

    T&& 右值引用 std::move

    右值引用出现之前我们只能用const引用来关联临时对象(右值)所以我们不能修临时对象的内容,右值引用的出现就让我们可以取得临时对象的控制权,终于可以修改临时对象了!

    int main()
    {
        int i = 42;
        int &r = i; // ok: r refers to i
        int &&rr = i;   // error: cannot bind an rvalue reference to an lvalue
        int &r2 = i * 42;   // error: i * 42 is an rvalue
        const int &r3 = i * 42; // ok: we can bind a reference to  const  to an rvalue
        int &&rr2 = i * 42;
        int &&rr3 = rr2;   // error: the expression rr2 is an lvalue!
        return 0;
    }
    

    即凡是可以 vartype varname; 这样定义出来的变量(variable)其自身都是左值。

    std::move相关。 右值引用因为绑定对象即将被销毁,意味着没有人会继续访问他们,所以就可以把他们(的资源)steal(偷)过来。 虽然不能将右值引用绑在左值上,但通过利用utility头文件新增的函数模板move,它返回传入对象的右值引用,可以达到 steal的效果。

        int &&rr3 = std::move(rr2); // ok
    

    再提醒:一旦使用了move,编译器就默认传入对象已经不打算使用了,是可以被销毁的,move之后该对象的值已经不确定,不要再访问。还有由于对象偷取与复制的差别巨大,不注意会产生非常难定位的bug,所以所有使用move的地方一定要使用全称std::move,给大家以提醒。(其实c++11在algorithm头文件也新增了一个move,参数与意义都与此截然不同)。

    #include <iostream>
    using namespace std;
    
    class HugeMem{
        public:
            HugeMem(int size): sz(size > 0 ? size : 1) {
                c = new int[sz];
            }
            ~HugeMem() { cout<<"HugeMem 析构\n";delete [] c; }
            HugeMem(HugeMem && hm): sz(hm.sz), c(hm.c) {
                cout<<"HugeMem move 构造\n";
                hm.c = nullptr;
            }
            int * c;
            int sz;
    };
    class Moveable{
        public:
            Moveable():i(new int(3)), h(1024) {}
            ~Moveable() { cout<<"Moveable 析构\n";delete i; }
            Moveable(Moveable && m):
                i(m.i), h(move(m.h)) {      // 强制转为右值,以调用移动构造函数
                    m.i = nullptr;
                }
            int* i;
            HugeMem h;
    };
    
    Moveable GetTemp() {
        //Moveable tmp = Moveable();
        Moveable tmp;
        cout << hex << "Huge Mem from " << __func__
            << " @" << tmp.h.c << endl; // Huge Mem from GetTemp @0x603030
        return tmp;
    }
    
    int main() {
        Moveable a(GetTemp());
        cout << hex << "Huge Mem from " << __func__
            << " @" << a.h.c << endl;   // Huge Mem from main @0x603030
    }
    

    早在C++11之前编译器就把优化几乎做到了极致——局部变量返回到函数外部并赋值给外部变量这个过程基本上不存在任何多余的临时变量构造和析构,这比move机制更加高效。显式指定move以后,return std::move(localvar)这里会强行从localvar移动构造一个临时变量temp,然后return temp(temp这里会有RVO优化)。

    auto for循环

    需要注意的是,auto不能用来声明函数的返回值。但如果函数有一个尾随的返回类型时,auto是可以出现在函数声明中返回值位置。这种情况下,auto并不是告诉编译器去推断返回类型,而是指引编译器去函数的末端寻找返回值类型。在下面这个例子中,函数的返回值类型就是operator+操作符作用在T1、T2类型变量上的返回值类型。

    template <typename T1, typename T2>
    auto compose(T1 t1, T2 t2) -> **decltype**(t1 + t2)
    {
       return t1+t2;
    }
    auto v = compose(2, 3.14); // v's type is double
    

    auto与for配合使用

    std::map<std::string, std::vector<int>> map;
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    map["one"] = v;
    
    for(const auto& kvp : map)
    {
      std::cout << kvp.first << std::endl;
    
      for(auto v : kvp.second)
      {
         std::cout << v << std::endl;
      }
    }
    
    int arr[] = {1,2,3,4,5};
    for(int& e : arr)
    {
      e = e*e;
    }
    

    std::lambda

    “Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。

    C++11 的 lambda 表达式规范如下:

    • [ capture ] ( params ) mutable exception attribute -> ret { body } (1)
    • [ capture ] ( params ) -> ret { body } (2)
    • [ capture ] ( params ) { body } (3)
    • [ capture ] { body } (4)

    其中

    (1) 是完整的 lambda 表达式形式, (2) const 类型的 lambda 表达式,该类型的表达式不能改捕获("capture")列表中的值。 (3)省略了返回值类型的 lambda 表达式,但是该 lambda 表达式的返回类型可以按照下列规则推演出来: 如果 lambda 代码块中包含了 return 语句,则该 lambda 表达式的返回类型由 return 语句的返回类型确定。 如果没有 return 语句,则类似 void f(...) 函数。 省略了参数列表,类似于无参函数 f()。

    [] // 不引用外部变量 [x, &y] // x引用方式 ,y 传值 [&] // 任何使用的外部变量都是引用方式。 [=] // 任何被使用到的外部都是传值方式。 [&, x] // 除x传值以外其他的都以引用方式。 [=, &z] // 除z引用以外其他的都是以传值方式使用。

    int main()
    {
        std::vector<int> c { 1,2,3,4,5,6,7 };
        int x = 5;
        c.erase(std::remove_if(c.begin(), c.end(), [x](int n) { return n < x; } ), c.end());
    
        std::cout << "c: ";
        for (auto i: c) {
            std::cout << i << ' ';
        }
        std::cout << '\n';
    
        // 可以用auto 接收一个lambda 表达式。
        auto func1 = [](int i) { return i+4; };
        std::cout << "func1: " << func1(6) << '\n';
    
        // std::function 也可以接收lambda 表达式。
    
        std::function<int(int)> func2 = [](int i) { return i+4; };
        std::cout << "func2: " << func2(6) << '\n';
    
        std::function<int()> func3 = [x]{return x;};
        std::cout << "func3: " << func3() << '\n';
    
    
        std::vector<int> someList = {1,2,3};  //这里是c++11
        int total = 0;
        double sum = 0.0f;
        std::for_each(someList.begin(), someList.end(), [&total](int x) { total += x; });
        std::cout << total << '\n';
        std::for_each(someList.begin(), someList.end(), [&](int x){ total += x; sum += x;});
        std::cout << total << '\n';
        std::cout << sum << '\n';
    
        //再写一种简单的lambda
        [](){std::cout<<"就地展开的lambda\n";}();
    
    }
    

    bind

    std::bind是STL实现函数组合概念的重要手段,std::bind绑定普通函数(函数指针)、lambda表达式、成员函数、成员变量、模板函数等

    #include <iostream>
    #include <functional>
    
    void f(int n1, int n2, int n3, const int& n4, int n5)
    {
            std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
    }
    
    int g(int n1)
    {
            return n1;
    }
    
    struct Foo {
        void print_sum(int n1, int n2)
        {
            std::cout << n1+n2 << '\n';
        }
        static void static_func(std::function<int(int)> f,int n)
        {
            std::cout<<"call static_func\n";
            std::cout<<"f(n):\t"<<f(n)<<"\n";
        }
        int data = 10; //c++11 支持声明是就初始化值
    };
    
    int main()
    {
        using namespace std::placeholders;
    
        // std::cref(n) 表示要把n以引用的方式传入  
        int n = 7;
        auto f1 = std::bind(f, _2, _1, 42, std::cref(n), n);
        n = 10;
        f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused
    
    
        // 绑定一个子表达式,用_3替换了 其他位置的变量
        // std::bind(g, _3) 在这里已经表示int
        auto f2 = std::bind(f, _4, std::bind(g, _4), _4, 4, 5);
        f2(10, 11, 12 ,13);
    
        // 绑定成员函数
        Foo foo;
        auto f3 = std::bind(&Foo::print_sum, foo, 95, _1);
        f3(5);
    
        // 绑定成员变量
        auto f4 = std::bind(&Foo::data, _1);
        std::cout << f4(foo) << '\n';
    
    
        // 绑定静态成员函数
        auto f5 = std::bind(&Foo::static_func,g,_1);
    
        f5(3);
    }
    

    std::function

    通过std::function对C++中各种可调用实体(普通函数、Lambda表达式、函数指针、以及其它函数对象等)的封装,形成一个新的可调用的std::function对象;让我们不再纠结那么多的可调用实体。

    转换后的std::function对象的参数能转换为可调用实体的参数; 可调用实体的返回值能转换为std::function对象的返回值。 std::function对象最大的用处就是在实现函数回调(实际工作中就是用到了这一点),使用者需要注意,它不能被用来检查相等或者不相等,但是可以与NULL或者nullptr进行比较。

    #include <functional>
    #include <iostream>
    using namespace std;
    
    std::function< int(int)> Functional;
    
    // 普通函数
    int TestFunc(int a)
    {
    return a;
    }
    
    // Lambda表达式
    auto lambda = [](int a)->int{ return a; };
    
    // 仿函数(functor)
    class Functor
    {
    public:
    int operator()(int a)
    {
    return a;
    }
    };
    
    // 1.类成员函数
    // 2.类静态函数
    class TestClass
    {
    public:
    int ClassMember(int a) { return a; }
    static int StaticMember(int a) { return a; }
    };
    
    int main()
    {
    // 普通函数
    Functional = TestFunc;
    int result = Functional(10);
    cout << "普通函数:"<< result << endl;
    
    // Lambda表达式
    Functional = lambda;
    result = Functional(20);
    cout << "Lambda表达式:"<< result << endl;
    
    // 仿函数
    Functor testFunctor;
    Functional = testFunctor;
    result = Functional(30);
    cout << "仿函数:"<< result << endl;
    
    // 类成员函数
    TestClass testObj;
    Functional = std::bind(&TestClass::ClassMember, testObj, std::placeholders::_1);
    result = Functional(40);
    cout << "类成员函数:"<< result << endl;
    
    // 类静态函数
    Functional = TestClass::StaticMember;
    result = Functional(50);
    cout << "类静态函数:"<< result << endl;
    
    return 0;
    }
    

    initializer_list

    过往,我们这样给vector赋值:

    std::vector v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    

    需要感谢的是,C++11让你更方便。

    std::vector v = { 1, 2, 3, 4 };
    

    这就是所谓的initializer list。更进一步,有一个关键字叫initializer list

    #include <iostream>
    #include <vector>
    #include <map>
    #include <string>
    #include <typeinfo>
    
    class MyNumber
    {
    public:
        MyNumber(const std::initializer_list<int> &v) {
            for (auto itm : v) {
                mVec.push_back(itm);
            }
        }
    
        void print() {
            for (auto itm : mVec) {
                std::cout << itm << " ";
            }
        }
    private:
        std::vector<int> mVec;
    };
    
    class Test {
    public:
        void show()
        {
            for(auto kv : nameToBirthday)
            {
                std::cout<<"key:\t"<<kv.first<<"\tvalue:\t"<<kv.second<<"\n";
            }
        }
    private:
        static std::map<std::string, std::string> nameToBirthday;
    };
    std::map<std::string,std::string> Test::nameToBirthday  = {
        {"lisi", "18841011"},
        {"zhangsan", "18850123"},
        {"wangwu", "18870908"},
        {"zhaoliu", "18810316"}
    };
    
    class CompareClass
    {
    public:
        CompareClass (int,int)
        {std::cout<<"call old const\n";}
        CompareClass (std::initializer_list <int> )
        {std::cout<<"call initializer_list const\n";}
    };
    
    
    int main()
    {
        MyNumber m = { 1, 2, 3, 4 };
        m.print();  // 1 2 3 4
    
        Test t;
        t.show();
    
        std::map<int,int> ii_map = {{1,1},{2,2}};
    
        CompareClass foo {10,20};  // calls initializer_list ctor
        CompareClass bar (10,20);  // calls first constructor
    
    
        for(auto kv : ii_map)
        {
            std::cout<<"key:\t"<<typeid(kv.first).name()<<"\n";
        }
        return 0;
    }
    

    注意

    本文内容转自博客:http://blog.csdn.net/tangliguantou/article/details/50549751

    相关文章

      网友评论

          本文标题:C++ STL标准模板库入门学习与应用

          本文链接:https://www.haomeiwen.com/subject/vaqapxtx.html