C++PrimerPlus学习之string类和标准模板库

string类

  • C++11新增的构造函数
    • 构造函数string(string &&str)
      • 类似于复制构造函数,导致新创建的stringstr的副本。但与复制构造函数不同的是,它不保证将str视为const。这种构造函数被称为移动构造函数。在有些情况下,编译器可使用它而不是复制构造函数,以优化性能。
    • 构造函数string(initializer_listil)
      • 使得能够将列表初始化语法用于string类。

        string piano_man={'L','i','s','z','t'};
        
    • 字符串种类
      • string库实际上是基于一个模板类的具体化。

        template<class charT,class traits=char _traits<charT>,class Allocator=allocator<charT> >
        basic_string{...};
        
      • 模板basic_string有4个具体化,每个具体化都有一个typedef名称:

        typedef basic_string<char> string;
        typedef basic_string<wchar_t> wstring;
        typedef basic_string<char16_t> u16string;//c++11
        typedef basic_string<char32_t> u32string;//c++11
        

智能指针模板类

  • 其作用为帮助管理动态内存分配,在函数终止后(不管是正常终止,还是由于出现了异常而终止),智能指针就会自动删除其指向的内存。

  • 使用方法

    • 包含头文件memory
    • 将指向xxx的指针替换为指向xxx的智能指针对象;
    • 删除delete语句
  • 一个例子

    #include
    using namespace std;
    void remodel(string &str)
    {
    	auto_ptr<string>ps(new string(str));
    	if(weird_thing())
    		throw exception();
    	str=*ps;
    	//delete ps; NO LONGER NEEDED
    	return;
    }
    
  • 有关智能指针的注意事项

    • 一个例子

      auto_ptr<string>ps(new string("hello"));
      auto_ptr<string>vocation;
      vocation=ps;
      
    • 如果psvocation是常规指针,则两个指针将指向同一个string对象。这是不能接受的,因为程序将试图删除同一个对象两次——一次是ps过期时,另一个是vocation过期时。要避免这种问题,方法有多种。

      • 定义赋值运算符,使之执行深复制。这样两个指针将指向不同的对象,其中的一个对象是另一个对象的副本。
      • 建立所有权(ownership)概念,对于特定的对象,只能有一个智能指针可拥有它,这样只有拥有对象的智能指针的构造函数会删除该对象。然后,将赋值操作转让所有权。这就是用于auto_ptrunique_ptr的策略。但unique_ptr的策略更严格。
      • 创建智能更高的指针,跟踪引用特定对象的智能指针数。这被称为引用计数(reference counting)。例如,赋值时,计数将加1,而指针过期时,计数将减1。仅当最后一个指针过期时,才调用delete。这是shared_ptr采用的策略。
    • 一个例子

      #include
      using namespace std;
      int main()
      {
          auto_ptr<string> films[3]=
          {
              auto_ptr<string>(new string("one")),
              auto_ptr<string>(new string("two")),
              auto_ptr<string>(new string("three"))
          };
      
          auto_ptr<string>pwin;
          pwin=films[1];
          for(int i=0;i<3;i++)
              cout<<*films[i]<<endl;
      }
      /*
      output:
      one
      Segmentation fault(core dumped)
      */
      

      auto_ptr改成unique_ptr后,编译器则会出现错误。

  • uniqu_ptr为何优于auto_ptr

    unique_ptr<string> p3(new string("auto"));
    unique_ptr<string>p4;
    p4=p3;//not allowed
    

    编译器将会认为这种语句非法,避免了p3不再指向有效数据的问题。因此,unique_ptrauto_ptr更安全(编译阶段错误比潜在的程序崩溃更安全)
    但有时候,将一个智能指针赋给另一个并不会留下危险的悬挂指针。

    unique_ptr<string> demo(const char *s)
    {
    	unique_ptr<string>temp(new string(s));
    	return temp;
    }
    unique_ptr<string>ps=demo("hello");
    

    demo()返回一个临时的unique_ptr。然后让ps接管了原本归返回的unique_ptr所有的对象,而返回的unique_ptr被销毁。
    总之,如果源unique_ptr是一个临时右值,编译器允许这么做;如果源unique_ptr将存在一段时间,编译器将禁止这么做。
    unique_str还有一个优点,即可以使用new[]delete[]的版本:

    unique_ptr< double[]>pda(new double[5]);//will use delete []
    
  • 选择智能指针

    • 如果程序要使用多个指向同一个对象的指针,应选择shared_ptr
    • 否则选择unique_ptr
    • 写函数的时候,参数列表不能值传递智能指针unique_ptr,必须使用引用传递

泛型编程

  • 迭代器类型

迭代器功能 输入 输出 正向 双向 随机访问
解除引用读取
解除引用写入
固定和可重复排序
++i i++
–i i–
i[n]
i+n
i-n
i+=n
i-=n
  • 输出流迭代器ostream_iterator模板

    #include
    ...
    ostream_iterator<int,char> out_iter(cout," ");
    

    out_iter迭代器现在是一个接口,使得能够使用cout来显示信息。第一个模板参数(这里是int)指出了被发送给输出流的数据类型;第二个模板参数(这里是char)指出了输出流使用的字符类型(另一个可能的值是wchar_t).构造函数的第一个参数(这里是cout)指出了要使用的输出流,它也可以是用于文件输出的流;最后一个字符串参数是在发送给输出流的每个数据项后显示的分隔符。

    copy(dice.begin(),dice.end(),out_iter);
    

    同理还有一个输入流迭代器istream_iterator与之类似。

  • 反向迭代器rbegin()rend()

    • rbegin()end()返回相同的值(超尾),但类型不同。同样rend()begin()也返回相同的值(指向第一个元素的迭代器),但类型不同。

    • 反向指针先递减,再解除引用

      #include
      using namespace std;
      int main()
      {
          int casts[10]={4,2,5,6,2,3,4,3,4,5};
          vector<int>dice(10);
          copy(casts,casts+10,dice.begin());
          ostream_iterator<int,char>out_iter(cout," ");
          copy(dice.begin(),dice.end(),out_iter);
          cout<<endl;
          copy(dice.rbegin(),dice.rend(),out_iter);
          cout<<endl;
          vector<int>::reverse_iterator ri;
          for(ri=dice.rbegin();ri!=dice.rend();++ri)
              cout<<*ri<<' ';
          cout<<endl;
      }
      /*
      output:
      4 2 5 6 2 3 4 3 4 5
      5 4 3 4 3 2 6 5 2 4
      5 4 3 4 3 2 6 5 2 4
      */
      
  • 插入迭代器

    • 插入新添加的元素,而不会覆盖已有的数据。并使用自动内存分配来确保能够容纳新的信息。

    • back_insert_iterator将元素插入到容器尾部

    • front_insert_iterator将元素插入到容器前端

    • insert_iterator将元素插到insert_iterator构造函数的参数指定的位置前面

    • 一些限制

      • back_insert_iterator只能用于允许在尾部快速插入的容器(时间固定)
      • front_insert_iterator只能用于允许在起始位置快速插入的容器(时间固定)
      • insert_iterator没有限制,因此可以用它把信息插入到vector的前端,然而,front_insert_iterator对于那些支持它的容器来说,完成任务的速度更快
    • 一个例子

      #include
      using namespace std;
      void show(const string &s){cout<<s<<' ';}
      int main()
      {
          string s1[4]={"hello","world","fish","fashion"};
          string s2[2]={"busy","bats"};
          string s3[2]={"silly","singers"};
          vector<string>words(4);
          copy(s1,s1+4,words.begin());
          for_each(words.begin(),words.end(),show);
          cout<<endl;
          copy(s2,s2+2,back_insert_iterator<vector<string> >(words));
          for_each(words.begin(),words.end(),show);
          cout<<endl;
          copy(s3,s3+2,insert_iterator<vector<string> >(words,words.begin()));
          for_each(words.begin(),words.end(),show);
          cout<<endl;
      }
      /*
      output:
      hello world fish fashion
      hello world fish fashion busy bats
      silly singers hello world fish fashion busy bats
      */
      

    容器种类

    • 存储在容器中的数据为容器所有,这意味着当容器过期时,存储在容器中的数据也将过期(然而,如果数据是指针的话,则它指向的数据并不一定过期)。
    • 不能将任何类型的对象存储在容器中,具体地说,类型必须是可赋值构造的和可赋值的。基本类型满足这些要求:只要类定义没有将复制构造函数和赋值运算符声明为私有或保护的,则也满足这种要求。

函数对象

  • 函数对象也叫函数符。函数符是可以以函数方式与()结合使用的任意对象。这包括函数名,指向函数的指针和重载了()运算符的类对象(即定义了函数operator()()的类)。
  • 函数符概念
    • 生成器(generator)是不用参数就可以调用的函数符
    • 一元函数(unary function)是用一个参数可以调用的函数符
    • 二元函数(binary function)是用两个参数可以调用的函数符
    • 返回bool值的一元函数是谓词(predicate)
    • 返回bool值的二元函数是二元谓词(binary predicate)
  • 一个例子
    #include
    using namespace std;
    bool cmp(int x,int y)
    {
        return x>y;
    }
    void show(int x){cout<<x<<endl;}
    int main()
    {
        int a[5]={2,4,1,3,5};
        //listb(a,a+5);
        sort(a,a+5,cmp);//二元谓词
        for_each(a,a+5,show);//一元函数
    }
    
    
  • 预定义的函数符
    • 一个例子

      #include
      using namespace std;
      int main()
      {
          plus<double>add;
          double y=add(2.2,3.4);
          vector<double>gr8{3.3,4.2,5.5};
          vector<double>m8{2.7,3.6,4.3};
          ostream_iterator<double,char>out(cout," ");
          transform(gr8.begin(),gr8.end(),m8.begin(),out,add);
          cout<<endl;
          transform(gr8.begin(),gr8.end(),m8.begin(),out,plus<double>());//使用默认构造函数创建一个对象
      }
      /*
      output:
      6 7.8 9.8
      6 7.8 9.8
      */
      
  • 自适应函数符和函数适配器
    • 有的时候函数需要接受一个一元函数参数的函数符,但自带函数符为二元函数时,这时候就需要使用bind1stbind2nd对象
      #include
      using namespace std;
      int main()
      {
          vector<double>gr8{3.3,4.2,5.5};
          vector<double>m8(3);
          ostream_iterator<double,char>out(cout," ");
          transform(gr8.begin(),gr8.end(),out,bind1st(multiplies<double>(),2.5));
      }
      /*
      output:
      8.25 10.5 13.75
      */
      
      

你可能感兴趣的:(C++学习,C++)