《C++ Primer Plus》学习笔记-string类和标准模板库

第16章 string类和标准模板库(本书附录G有详细介绍)

16.1 string类

string类是由头文件string支持的。
string类位于命名空间std中。
ctor标识是传统C++中构造函数的缩写。NBTS是以空字符结束的传统C字符串的缩写。

16.1.1 构造字符串(string的构造函数)

《C++ Primer Plus》学习笔记-string类和标准模板库_第1张图片
可以使用+运算符直接将两个string对象拼接。

16.1.2 string类输入(两种方式)

string stuff;
cin >> stuff; // 第一种方式
getline(cin,stuff); // 第二种方式

//输出方式
cout << stuff;

16.1.3 使用字符串string(基本功能)

  1. 关系运算
      string对六个关系运算符(==,!=,<,<=,>,>=)均进行了三种重载,能够将string对象与另一个string对象、C风格字符串进行比较,并能够使C风格字符串与string对象进行比较。
  2. 确定字符串的长度
      size()和length()成员函数都返回字符串中的字符数。
  3. 查找
      查找string对象当中相应字符或字符串出现的情况。
    《C++ Primer Plus》学习笔记-string类和标准模板库_第2张图片《C++ Primer Plus》学习笔记-string类和标准模板库_第3张图片
    find()查找字符串或字符首次出现的位置;
    rfind()查找字符串或字符最后一次出现的位置;
    find_first_of()查找参数中任何一个字符首次出现的位置;
    find_last_of()查找参数中任何一个字符最后一次出现的位置;
    find_first_not_of()查找第一个不包含在参数中的字符出现的位置。
// 寻找hark中任意一个字母h、a、r、k第一次在stuff中出现的位置
int where = stuff.find_first_of("hark");

16.2 智能指针模板类

智能指针是行为类似于指针的类对象,并且该对象还具有其他功能。
要创建智能指针对象,必须包含头文件memory。
智能指针位于命名空间std中。

16.2.1 使用智能指针

  三个智能指针模板(auto_ptr、unique_ptr、shared_ptr)都定义了类似指针的对象,可以将new获得的地址赋给这些对象;在智能指针过期时,这些内存将自动被释放(不需要手动delete)。
初始化智能指针

// 初始化指向double类型的智能指针
auto_ptr<double> pd(new double);
auto_ptr<double> pd = new double;
unique_ptr<double> p(new double);
shared_ptr<double> pi(new double);

注意所有智能指针类都有一个explicit构造函数,因此不能隐式将普通指针转换为智能指针,需要进行显式转换。

shared_ptr<double> pd;
double *p_reg = new double;
pd = shared_ptr<double>(p_reg);

智能指针对象可以进行类似于常规指针的操作:解除引用操作(*pd)、用它来访问结构成员(pd->puffIndex)、将它赋值给指向相同类型的常规指针。
注意智能指针指向的内存必须由new分配,因为智能指针最后将调用delete。

16.2.2 有关智能指针的注意事项

  1. 赋值操作
      同类型智能指针之间进行赋值有时会出现问题。为了避免赋值后多个智能指针指向同一个地址,从而对同一个地址多次使用delete方法,三种智能指针给出了两种不同的策略。
      auto_ptr和unique_ptr建立所有权概念,当使用赋值运算符时,所有权从一个智能指针转移到另一个智能指针,释放所有权的智能指针不再指向原来的内存,成为一个空指针。
      二者的区别在于,auto_ptr允许所有情况下的赋值操作,而unique_ptr对何时可以进行赋值操作存在限制,仅当原智能指针是个临时右值,才允许进行赋值操作,如果原智能指针还将存在一段时间,则不允许赋值操作。
      shared_ptr跟踪引用特定对象的智能指针数,称为引用计数。当进行赋值操作时,计数将加1,指针过期时,计数将减1.仅当最后一个指针过期时,才调用delete。
  2. new和new[]
      在shared_ptr和auto_ptr只能赋值new分配内存的地址;
      在unique_ptr中既可以赋值使用new分配内存的地址,也可以赋值使用new[]分配内存的地址。

16.2.4 选择智能指针

  如果程序需要使用多个指向同一个对象的指针,则使用shared_ptr(如果编译器没有提供,可以使用Boost库提供的shared_ptr)。
  如果程序不需要多个指向同一个对象的指针,则可以使用unique_ptr。

16.3 标准模板库STL(本书附录G有详细介绍)

STL提供了一组表示容器、迭代器、函数对象和算法的模板。
容器是一个与数组类似的单元,可以存储若干个值。STL的容器是同质的,即存储的值的类型相同;
算法是完成特定任务(如对数组进行排序)的处方;
迭代器能够用来遍历容器的对象,与遍历数组的指针类似,是广义指针(可对其执行类似指针的操作,如解除引用和递增等),通过将指针广义化为迭代器,能够为各种不同的容器类(包括哪些简单指针无法处理的类)提供统一的接口;
函数对象是类似于函数的对象,可以是类对象或函数指针。

16.3.1 模板类vector

此处的vector(矢量)对应数组;
可以进行vector对象的创建,将一个vector对象赋给另一个vector对象,使用[]来访问vector元素;
vector模板使用动态内存分配,可以用初始化参数来指出需要多少矢量;
使用表示法来指出要使用的类型。

// 包含vector的头文件
#include 
// vector在std中
using namespace std;
// 初始化元素为int类型的vector
vector<int> ratings(5);
// 通过[]来访问vector元素
ratings[0] = 9;

16.3.2 可对vector执行的操作

size()返回容器中元素数目;
swap()交换两个容器的内容;
begin()返回一个指向容器中第一个元素的迭代器;
end()返回一个表示超过容器尾的迭代器(指向容器最后一个元素后面的那个元素位置);
push_back()将元素添加到矢量(vector)末尾;
erase()删除矢量中给定区间的元素;
insert()插入指定区间的元素到指定位置;

  1. 迭代器
    初始化并简单使用迭代器
// 初始化一个double容器的迭代器
vector<double>::iterator pd;
// 初始化一个double容器
vector<double> scores;
// 将迭代器指向容器pd的首元素
pd = scores.begin();
// 通过迭代器给元素赋值(类似指针)
*pd = 22.3;
// 将迭代器指向下一个元素
++pd;

此处可以使用自动类型推断来定义合适类型的迭代器

vector<double>::iterator pd = scores.begin(); // 直接定义pd
auto pd = scores.begin(); // 使用auto定义pd
  1. 添加元素
double temp;
cin >> temp;
scores.push_back(temp); // 将temp添加到double矢量scores队尾
  1. 删除元素
    erase()第一个参数为指向区间起始处的迭代器,第二个参数为指向区间终值处后一个位置的迭代器,即是一个左闭右开区间。
// 删除sorces容器当中begin()和begin()+1指向的元素
scores.erase(scores.begin(),scores.begin()+2);
  1. 插入元素
    insert()接受三个迭代器参数,第一个参数指定了新元素插入的位置(新元素将插入到第一个参数对应的迭代器的前面),第二个和第三个迭代器参数定义了被插入区间(左闭右开),该区间通常是另一个容器对象的一部分。
vector<int> old_v;
vector<int> new_v;
...
// 将new_v中除第一个元素外的所有元素插入到old_v的第一个元素前面
old_v.insert(old_v.begin(),new_v.begin()+1,new_v.end());

16.3.3 对矢量(vector)可执行的其他操作

STL从更广泛的角度定义了非成员函数,即定义了使用于所有容器的函数。
比较有代表性的三个函数:
for_each()接受三个参数,前两个是定义容器中区间的迭代器(规定了容器对象,即迭代器指向的对象),最后一个是指向函数的指针(一个函数对象)。该非成员函数将被指向的函数应用于容器区间中的各个元素。被指向的函数不能修改容器元素的值。(该函数可以用于任何容器)
random_shuffle()接收两个指定区间的迭代器参数,并随机排列该区间内的所有元素。(该函数要求容器可以随机访问)
sort()有两个版本(该函数要求容器可以随机访问)
  第一个版本接收两个定义区间的迭代器参数,并使用为存储在容器中的类型元素定义的<运算符,对区间中的元素进行操作,按照定义的<以升序进行排序。(如vector中的元素是stuff类,则在stuff类中提供成员或非成员函数operator<()即可时用sort()对此类容器进行排序)
  第二个版本接收三个参数,前两个参数是指定区间的迭代器,最后一个参数是指向要使用的函数指针(函数对象)。该函数对象的返回值转换成bool型,false表示顺序不正确。

16.3.4 基于范围的for循环(C++11)

基于范围的for循环是为用于STL而设计的。

// 假设已经实例化了容器books和不修改元素的函数ShowReview、修改元素的函数InflateReview
// 使用for_each对其中元素进行操作
for_each(books.begin(),books.end(),ShowReview)
// 使用基于范围的for循环进行相同操作
for(auto x : books) ShowReview(x);
// 将类型设置为引用可以直接修改容器中的元素
for(auto & x :books) InflateReview(x);

16.4 泛型编程

泛型编程旨在编写独立于数据类型的代码。
迭代器是广义指针,而指针满足所有迭代器要求。STL算法可以使用指针来对基于指针的非STL容器进行操作。

16.4.4 概念、改进和模型

  1. 使用ostream_iterator迭代器输出STL容器的内容
#include 
#include 
using namespace std;
...
ostream_iterator<int,char> out_iter(cout," "); 
*cout_iter++ = 15; // 类似 cout << 15 << " "; 并为下一次输出做好准备
copy(dice.begin(),dice.end(),out_iter); // 容器dice该区间中的内容
--------------------------------------------------------------------------
// 也可以使用匿名迭代器如下
copy(dice.begin(),dice.end(),ostream_iterator<int,char>(cout," "));
  1. 使用insert_iterator表示插入或复制到的位置
insert_iterator<vector<int>> insert_iter(dice,dice.begin());

创建用于指示将数据插入到vector类型容器dice首元素之前的insert_iterator迭代器。其中第一个参数表示容器变量,第二个参数表示要插入的位置。

16.4.5 容器种类

  1. list(在头文件list中声明)
    list模板类表示双向链表。
list<int> one; // 实例化一个list

《C++ Primer Plus》学习笔记-string类和标准模板库_第4张图片
merge()合并两个链表并排序,保存到调用链表当中,清空另一个链表;
remove()从链表当中删除所有val元素(如删除整数链表当中所有的2);
sort()排序;
splice()将链表x插入到调用链表的pos前面,并清空x;
unique()将连续的相同元素合并成一个(如将连续的多个元素2合并成一个元素2)。

  1. queue(在头文件queue中声明)
    queue模板类是一个适配器类,它把使用限制在定义队列的基本操作上。
    《C++ Primer Plus》学习笔记-string类和标准模板库_第5张图片
  2. priority_queue(在头文件queue中声明)
    是一个适配器类,它支持的操作与queue相同。区别在于priority_queue中最大的元素被移动到队首。可以通过提供一个可选的构造函数参数,修改用于确定那个元素放到队首的比较方式。
priority_queue<int> pq1; // 默认比较方式
priority_queue<int> pq2(greater<int>); // 通过参数greater修改比较方式,如对于string元素使用greater
  1. stack(在头文件stack中声明)
    是一个适配器类,它把使用限制在定义栈的基本操作上。
    《C++ Primer Plus》学习笔记-string类和标准模板库_第6张图片
  2. array(在头文件array中定义)
    并非STL容器,因为其长度是固定的,因此没有调整大小的操作。但可以将很多标准STL算法用于array对象(如copy()for_each())。

16.4.6 关联容器

关联容器将值与键关联在一起,并使用键来查找值。
对于容器X,表达式X::value_type指出了容器中值的类型,对于关联容器来说,表达式X::key_type指出了键的类型。
STL提供了四种关联容器set、multiset(在头文件set中定义),map、multimap(在头文件map中定义)。

set值类型与键相同,键是唯一的,集合不会有多个相同的键(值)。对于set来说值就是键;
multiset类似于set,只是可能有多个值的键相同;
map值与键的类型不同,键是唯一的,每个键只对应一个值;
multimap与map相似,但一个键可以与多个值相关联。

  1. set(集合)
    (1)实例化一个set

    set<string> A;
    set<string,less<string>> A; // 若为int集合则应使用less
    

    第二个参数是可选的,用于指示用来对键进行排序的比较函数或对象。
    (2)初始化并显示set

    const int N = 6;
    string s1[N] = {"buffoon","thinkers","for","heavy","can","for"};
    set<string> A(s1,s1+N); // 使用指针作为迭代器参数,将s1的所有元素赋值给关联容器A
    // 关联容器会自动将元素排序,并合并相同的元素
    // 输出A
    ostream_iteratro<string,char> out(cout," ");
    copy(A.begin(),A.end(),out);
    -------------------------------------------------------------------------------
    结果:buffoon can for heavy thinkers
    

    (3)基本使用方法
    set_union()求并集;
    set_intersection()求交集;
    set_difference()两个集合相减,消除第一个集合中包含在第二个集合里的元素;
    lower_bound()将键作为参数返回一个迭代器,该迭代器指向集合中第一个不小于键参数的成员;
    upper_bound()将键作为参数返回一个迭代器,该迭代器指向集合中第一个大于键参数的成员;
    insert()插入元素,可以插入单个元素,也可以插入一个集合区间中的所有元素。

    set_union()、set_intersection()、set_difference()均接受5个迭代器参数,其中前两个迭代器定义了第一个集合的区间(记为集合A),接下来的两个迭代器定义了第二个集合区间(记为集合B),最后一个迭代器是输出迭代器

    函数 功能
    set_union() A ∪ B A\cup B AB
    set_intersection() A ∩ B A\cap B AB
    set_difference() A − A ∩ B A-A\cap B AAB

    其中输出迭代器可以通过cout显示也可以输出到其他的集合当中

    // 显示合并集合
    set_union(A.begin(),A.end(),B.begin(),B.end(),ostream_iterator<string,char> out(cout," "));
    // 将合并的集合保存到集合C中,插入到C的首个元素前面
    set_union(A.begin(),A.end(),B.begin(),B.end(),ostream_iterator<set<string>>(C,C.begin()));
    

    使用insert()插入元素并使用lower_bound()、upper_bound()进行输出

    set<string> C;
    string s1[6] = {"any","buffoon","can","deliver","elegant","food","for","grungy","heavy","metal","thinkers"};
    C.insert(s1,s1+6);
    copy(C.lower_bound("ghost"),C.upper_bound("spook"),ostream_iterator<string,char> out(cout," "));
    -----------------------------------------------------------------------------------------------------------
    结果: grungy heavy metal
    
  2. multimap
    (1)创建multimap对象
    创建一个键为int类型,值为string类型的multimap对象;
    第三个参数为可选参数,指出用于对键进行排序的比较函数或对象,multimap按键排序。

    multimap<int,string> codes; 
    

    (2)创建值并插入到multimap对象

    // 创建codes对象存储类型的值item  pair
    pair<const int,string> item(213,"Los Angeles");
    // 将pair插入到codes
    codes.insert(item);
    
    //直接插入匿名对象
    codes.insert(pair<const int,string> (213,"Los Angeles"));
    
    // 可以使用first、second访问pair对象的两个部分
    cout << item.first << ' ' << item.second << endl;
    

    (3)获取multimap对象的信息
    count()接收键作为参数,返回具有该键的元素数目
    lower_bound()同set
    upper_bound()同set
    equal_range()接收键作为参数,返回两个迭代器,他们表示的区间与该键匹配(返回区间中的元素的键均为该参数值)。为返回两个参数,将两个迭代器放在一个pair对象中。

    打印codes中键为416的所有城市

    pair<multimap<int,string>::iterator,multimap<int,string>::iterator> range = codes.equal_range(416);
    // 定义指向multimap类型的对象中元素的迭代器
    std::multimap<int,string>::iterator it;
    for(it = range.first; it != range.second; ++it)
    	cout << (*it).second <<endl;
    
    // 可以使用自动类型推断简化代码
    auto range = codes.equal_range(416);
    for(auto it = range.first; it != range.second; ++it)
    	cout << (*it).second <<endl;
    

16.5 函数对象

函数对象(函数符)是可以以函数方式与()结合使用的任意对象。函数名、指向函数的指针、重载了()运算符的类对象都可以当做函数符使用。
使用函数符f的函数,会响应得调用f()
可以使用类将多参数函数转换成单参数形式。

16.6 算法

  

你可能感兴趣的:(笔记,c++,学习)