C++基础(下Ⅱ)

  • 关联容器
  • set / multiset ; map / multimap(是否允许有相同的key值)

  • 内部元素有序排列,新元素插入的位置取决于它的值,查找速度快。

  • 除了各容器都有的函数外,还支持以下成员函数:
    find: 查找等于某个值 的元素(x小于y和y小于x同时不成立即为相等)
    lower_bound : 查找某个下界
    upper_bound : 查找某个上界
    equal_range : 同时查找上界和下界
    count :计算等于某个值的元素个数(x小于y和y小于x同时不成立即为相等)
    insert: 用以插入一个元素或一个区间

    // 预备知识: pair 模板
    template <class _T1, class _T2> struct pair {
        typedef _T1 first_type;
        typedef _T2 second_type;
        _T1 first;
        _T2 second;
    
        // 无参构造函数
        pair() : first(), second() {} 
        // 有参构造函数
        pair(const _T1 &__a, const _T2 &__b) : first(__a), second(__b) {}
        // 模板构造函数
        template <class _U1, class _U2>
        pair(const pair<_U1, _U2> &__p) : first(__p.first), second(__p.second) {}
    };
    /**
    map/multimap容器里放着的都是pair模版类的对象,且按first从小到大排序
    
    第三个构造函数用法示例:
        pair<int,int> p( pair<double,double>(5.5,4.6) );
        // 结果p.first = 5, p. second= 4
    */
    
    template <class Key, class Pred = less<Key>, class A = allocator<Key> >
    
    class multiset { };
    
  • Pred类型的变量决定了multiset 中的元素,“一个比另一个小”是怎么定义的。
    multiset运行过程中,比较两个元素x,y的大小的做法,就是生成一个 Pred类型的
    变量,假定为 op,若表达式op(x,y) 返回值为true,则 x比y小。Pred的缺省类型是 less< key>

    // less 模板的定义:
    template<class T>
    struct less : public binary_function<T, T, bool> { 
    	// 重载了 ()
    	bool operator() (const T& x, const T& y) const { 
    		return x < y ; 
    	} 
    };
    //less模板是靠 < 来比较大小的
    
  • multiset的成员函数
    iterator find(const T & val):在容器中查找值为val的元素,返回其迭代器。如果找不到,返回end()。
    iterator insert(const T & val):将val插入到容器中并返回其迭代器。
    void insert( iterator first,iterator last):将区间 [first,last) 插入容器。
    int count(const T & val): 统计有多少个元素的值和val相等。
    iterator lower_bound(const T & val):查找一个最大的位置 it,使得 [begin(),it) 中所有的元素都比 val 小。
    iterator upper_bound(const T & val):查找一个最小的位置 it,使得 [it,end()) 中所有的元素都比 val 大。
    pair<iterator,iterator> equal_range(const T & val):同时求得lower_bound和upper_bound。
    iterator erase(iterator it):删除it指向的元素,返回其后面的元素的迭代器(Visual studio 2010上如此,但是在C++标准和Dev C++中,返回值不是这样)。

  • 容器适配器:
  • stack:是后进先出的数据结构,只能插入,删除,访问栈顶的元素。可用 vector, list, deque来实现。缺省情况下,用deque实现。用 vector和deque实现,比用list实现性能好。

  • queue:和stack 基本类似,可以用 list和deque实现。缺省情况下用deque实现。

  • priority_queue:和 queue类似,可以用vector和deque实现。缺省情况下用vector实现。 priority_queue 通常用堆排序技术实现,保证最大的元素总是在最前面。即执行pop操作时,删除的是最大的元素;执行top操作时,返回的是最大元素的常引用。默认的元素比较器是less。push、pop 时间复杂度O(logn) top()时间复杂度O(1)
    默认大顶堆,即输出top为降序;priority_queue<double,vector<double>,greater < double> > pq 比较方式为greater 则为小顶堆输出为升序

  • 容器适配器的元素个数
    stack,queue,priority_queue 都有:empty() 成员函数用于判断适配器是否为空;size() 成员函数返回适配器中元素个数

  • STL中的算法大致可以分为以下七类:

    1)不变序列算法
    2)变值算法
    3)删除算法
    4)变序算法
    5)排序算法
    6)有序区间算法
    7)数值算法

  • stable_sort:将区间从小到大排序,并保持相等元素间的相对次序(可自定义
    比较器)即相等的元素不会乱序保持原来的相对位置。

  • C++11特性:
    • 统一的初始化方法
    • 成员变量默认初始值
    • auto关键字:用于定义变量,编译时可以自动判断变量的类型
    • decltype 关键字
      // 求表达式的类型
      int i;
      double t;
      struct A { double x; };
      const A* a = new A();
      decltype(a) x1; // x1 is A *
      decltype(i) x2; // x2 is int
      decltype(a->x) x3; // x3 is double
      decltype((a->x)) x4 = t; // x4 is double&
      
    • 智能指针shared_ptr
    1. 头文件:
    2. 通过shared_ptr的构造函数,可以让shared_ptr对象托管一个new运算符返回的指针,写法如下:shared_ptr<T> ptr(new T); // T 可以是 int ,char, 类名等各种类型;此后ptr就可以像 T* 类型的指针一样来使用,即 *ptr 就是用new动态分配的那个对象,而且不必操心释放内存的事
    3. 多个shared_ptr对象可以同时托管一个指针,系统会维护一个托管计数。当无shared_ptr托管该指针时,delete该指针。
    4. shared_ptr对象不能托管指向动态分配的数组的指针,否则程序运行会出错
    • 空指针nullptr

    • 基于范围的for循环

    • 右值引用(&&)和move语义【右值:一般来说,不能取地址的表达式,就是右值,
      能取地址的,就是左值
      】主要目的是提高程序运行的效率,减少需要进行深拷贝的对象进行深拷贝的次数。

      class A { };
      A & r = A(); // error , A()是无名变量,是右值;临时变量
      A && r = A(); //ok, r 是右值引用
      
    • 函数返回值为对象时,返回值对象如何初始化?

    • 只写复制构造函数
      return 局部对象 -> 复制
      return 全局对象 -> 复制

    • 只写移动构造函数
      return 局部对象 -> 移动
      return 全局对象 -> 默认复制
      return move(全局对向) -> 移动

    • 同时写 复制构造函数和 移动构造函数:
      return 局部对象 -> 移动
      return 全局对象 -> 复制
      return move(全局对向) -> 移动

    #include <cstring>
    #include <iostream>
    #include <string>
    using namespace std;
    class String {
      public:
        char *str;
    
        // 无参构造函数
        String() : str(new char[1]) { str[0] = 0; }
        // 复制构造函数1
        String(const char *s) {
            str = new char[strlen(s) + 1];
            strcpy(str, s);
        }
        // 复制构造函数2
        String(const String &s) {
            cout << "copy constructor called" << endl;
            str = new char[strlen(s.str) + 1];
            strcpy(str, s.str);
        }
        // 赋值 = 构造函数
        String &operator=(const String &s) {
            cout << "copy operator= called" << endl;
            if (str != s.str) {
                delete[] str;
                str = new char[strlen(s.str) + 1];
                strcpy(str, s.str);
            }
            return *this;
        }
        // move constructor
        // 相当于直接把 str 指向了s 的存储空间,再把s.str 指向一个空值(右值会修改原来的变量,故最好用于临时变量)
        String(String &&s) : str(s.str) {
            cout << "move constructor called" << endl;
            s.str = new char[1];
            s.str[0] = 0;
        }
    
        // move assigment
        String &operator=(String &&s) {
            cout << "move operator= called" << endl;
            if (str != s.str) {
                delete[] str;
                str = s.str;
                s.str = new char[1];
                s.str[0] = 0;
            }
            return *this;
        }
        ~String() { delete[] str; }
    };
    
    template <class T> void MoveSwap(T &a, T &b) {
        T tmp(move(a));          // std::move(a)为右值,这里会调用move
        constructor a = move(b); // move(b)为右值,因此这里会调用move assigment
        b = move(tmp);           // move(tmp)为右值,因此这里会调用move
        assigment
    }
    
    int main() {
        // String & r = String("this"); // error
        String s;
        s = String("ok"); // String("ok")是右值
        cout << "******" << endl;
        String &&r = String("this");
        cout << r.str << endl;
        String s1 = "hello", s2 = "world";
        MoveSwap(s1, s2);
        cout << s2.str << endl;
        return 0;
    }
    /**
     * 输出:
    move operator= called
    ******
    this
    move constructor called
    move operator= called
    move operator= called
    hello
    */
    
    • Lambda表达式
      只使用一次的函数对象,能否不要专门为其编写一个类?
      只调用一次的简单函数,能否在调用时才写出其函数体?
      形式:
      [ 外部变量访问方式说明符 ] (参数表) -> 返回值类型 {
      语句组
      }

      [ ] 不使用任何外部变量
      [ = ] 以传值的形式使用所有外部变量
      [ & ] 以引用形式使用所有外部变量
      [ x, &y ] x 以传值形式使用,y 以引用形式使用
      [ =, &x, &y ] x,y 以引用形式使用,其余变量以传值形式使用
      [ &, x, y ] x,y 以传值的形式使用,其余变量以引用形式使用
      " -> 返回值类型 " 也可以没有, 没有则编译器自动判断返回值类型。

    • 多线程
      #include <thread>

    • 类型强制转换

    1. static_cast
      static_cast用来进用行比较“自然”和低风险的转换,比如整型和实数型、字符型之间互相转换。static_cast不能来在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,也不能用于不同类型的引用之间的转换。
    2. reinterpret_cast
      reinterpret_cast用来进行各种不同类型的指针之间的转换、不同类型的引用之间转换、以及指针和能容纳得下指针的整数类型之间的转换。转换的时候,执行的是逐个比特拷贝的操作。
    3. const_cast
      用来进行去除const属性的转换。将const引用转换成同类型的非 const引用,将const指针转换为同类型的非const指针时用它。
    4. dynamic_cast
      dynamic_cast专门用于将多态基类的指针或引用,强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回NULL指针dynamic_cast不能用于将非多态基类的指针或引用,强制转换为派生类的指针或引用
    • 异常处理
      用try、catch进行异常处理;
      注意:try块中定义的局部对象,发生异常时会析构!
      一旦执行throw 语句(余下的语句就不会再执行了)就会跳到catch块执行;没有抛出异常的话catch块就不会执行。
    • 运行时类型检查
      C++运算符typeid是单目运算符,可以在程序运行过程中获取一个表达式的值的类型。typeid运算的返回值是一个type_info类的对象,里面包含了类型的信息。typeid(变量名)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值