string类和标准模板库

string 类

构造字符串

在这里插入图片描述

#include <iostream>
#include<string>
using namespace std;

int main()
{
	string str("hello");  //1
	cout << str << endl;
	string s2(10, 'w'); //2
	cout << s2 << endl;
	string s3(s2);   //3
	cout << s3 << endl;
	s3 += "add";
	cout << s3 << endl;
	s3[0] = 'p';
	cout << s3 << endl;
	string s4; //4
	s4 = s2 + s3;
	cout << s4 << endl;
	char cs[] = "this is a test";
	string s6(cs,18);  //5
	cout << s6 << endl;
	string s7(cs + 4, cs + 8);  //6
	cout << s7 << endl;
	string s8(&s6[4], &s6[8]);   //6
	cout << s8 << endl;
	string s9(str, 1, 3);    //7
	cout << s9 << endl;
	return 0;
}

输出:
hello
wwwwwwwwww
wwwwwwwwww
wwwwwwwwwwadd
pwwwwwwwwwadd
wwwwwwwwwwpwwwwwwwwwadd
this is a test
is
is
ell

  1. C++11新增
    string str = {‘1’,‘2’};

string类输入

1.c风格的字符串,有三种方式输入
char info[100];
cin>>info;
cin.getline(info,100);
cin.get(info,100);
2.string对象有两种
string str;
cin>>str;
getline(cin,str);
两个版本的getline()都有一个可选参数,用于指定使用哪个字符来确定输入边界
cin.getline(info,100,’:’);读到:并丢弃 cin为调用对象
getline(str,’:’); 函数,cin为参数 自动调整string的大小
限制:
1.string对象的最大允许长度,由常量 string::npos指定。通常是最大的unsigned int 值。(试图将整个文件读入string中,可能有问题)
2.程序可以使用的内存量
string 的getline()从输入中读取字符,并存储在string中,直到发生以下情况。
1.到达文件尾,此时输入流的eofbit将被设置,意味着方法fail()和eof()都返回true;
2.遇到分界字符,默认(\n),这种情况,将把分界字符从输入流中删除,但不存储它。
3.读取的字符达到最大允许值(string::npos和可供分配的内存字节数中较小的一个),这种情况下,将设置输入流的failbit意味着方法fail()将返回true。
string的operator>>()函数的行为与此类似,只是它不断读取,直到遇到空白字符,并将其留在输入队列中,而不是不断读取,直到遇到分解字符并将其丢弃。空白字符指空格,换行和制表符,更简单的说是调用isspace()是,返回true的字符。

#include <iostream>
#include<string>
#include<fstream>
#include<cstdlib>
using namespace std;

int main()
{
	ifstream fin;
	fin.open("123.txt");
	if (fin.is_open() == false)
	{
		cout << "open error";
		exit(EXIT_FAILURE);
	}
	string str;
	int count = 0;
	getline(fin, str,':');
	while (fin)
	{
		++count;
		cout <<count<<" : "<< str << endl;
		getline(fin, str, ':');
	}
	fin.close();

	return 0;
}

在这里插入图片描述
默认\n为分界符,指定:为分界符后,\n将被视为常规字符,因此5输出空 和safa。

使用字符串

string比较,如果在机器排列序列中,一个对象位于另一个对象前面,则前者被视为小于后者。如果机器排列为ASCII码,则数字将小于大写字符,大写字符小于 小写字符。可以使用如下的比较
string s1(“aaa”);
string s2(“bbb”);
char a[30] = “sssss”;
s1<s2;s2<a;s2 != a;
在这里插入图片描述
在这里插入图片描述
rfind(),find_first_of(),find_last_of(),find_last_not_of(),find_first_not_of(),它们重载的特征标与find方法相同。rfind()查找子字符串或字符最后一次出现的位置。
find_first_of()在字符串中查找参数中任何一个字符首次出现的位置。s1= cobra; pos =find_first_of(“hark”) = 3;因为hark中各个字符在 cobra中首次出现的位置3.
find_first_of()在字符串中查找参数中任何一个字符最后出现的位置 pos =find_last_of(“hark”) = 4,a最后一次
find_first_not_of()在字符串中查找第一个不包含在参数中的字符。pos =find_first_not_of(“hark”) = 0 , 即c的位置。

int main()
{
	string tmp = "1234567890";
	int index = 0;
	string str = "345";     //从0开始
	index = tmp.find(str);   //2
	index = tmp.find(str,1);  //2
	index = tmp.find("56");    //4
	index = tmp.find("5678",1,2); //4
	index = tmp.find("0");     //9
}

capacity()返回当前分配给字符串的内存块的大小。
reverse()设置能够请求内存块的最小长度。
string类实行动态内存分配,若不够,则在原来的基础上翻倍,并重新开辟内存。

字符串种类

在这里插入图片描述
在这里插入图片描述

智能指针

使用智能指针

auto_ptr,unique_ptr,shared_ptr都定义了类似指针的对象,可以将new获得(直接或间接)的地址赋给这种对象。当智能指针对象过期时,其析构函数将使用delete 来释放内存。
在这里插入图片描述
创建使用智能指针,包含头文件
在这里插入图片描述
throw()意味着构造函数不会引发异常,与auto_ptr一样,throw也被抛弃。
auto_ptr pd (new double);

#include <iostream>
#include<string>
#include<memory>
using namespace std;

class Report {
private:
    string str;
public:
    Report(const string s) :str(s) { cout << str << " created \n"; }
    ~Report()
    {
        cout << str << " delete \n";
    }
    void comment() const { cout << str << endl; }
};
int main()
{
    std::cout << "Hello World!\n";
    {
        auto_ptr<Report> ps(new Report("use auto"));
        ps->comment();
    }
    {
        shared_ptr<Report> ps(new Report("use share"));
        ps->comment();
    }
    {
        unique_ptr<Report> ps(new Report("use unique"));
        ps->comment();
    }
}

  1. 所有智能指针类都有一个explicit构造函数,该构造函数将指针作为参数。因此不需要自动将指针转为智能指针对象。
    shared_ptr pd;
    double* p_reg = new double;
    pd = p_reg; // not allowed
    pd = shared_ptr(p_reg); //ok
    shared_ptr ps = p_reg; // not allowed
    shared_ptr ps (p_reg); //ok
  2. 智能指针与常规指针类似,可以 ps-> 或者*ps
  3. 智能指针应该避免
    string s(“safsgag”);
    shared_ptr ps(&s);
    ps过期时,程序将把delete运算符用于非堆的内存

智能指针注意事项

实际上有四种,还有weak_ptr,为何摒弃auto_prt;
auto_ptr ps(new string(“sssss”));
auto_ptr pd ;
ps = pd;
如果ps和pd是常规指针,则两个指针将指向同一个string对象。这样程序将试图删除同一个对象两次,一次是ps过期,一次是pd过期。避免这种问题方法有很多。
1.定义赋值运算,使之执行深度赋值。这样两个指针将指向不同的对象,其中的一个对象是另一个对象的副本
2.建立所有权概念,对于特定的对象,只有一个智能指针能拥有它。这样只有拥有对象的智能指针的析构函数会删除该对象。然后让赋值操作转让所有权,这就是用于auto和unique的策略,但后者更严格。
3.创建更高的指针,跟踪引用特定对象的智能指针数。这称为引用计数。赋值时,计数+1,对象过期时-1。仅当最后一个对象过期时,才调用delete。(shared_ptr)
4. 不适用auto_ptr的例子

int main()
{
    auto_ptr<string> a[4] =
    {
        auto_ptr<string>(new string("088")),
        auto_ptr<string>(new string("134")),
        auto_ptr<string>(new string("5667")),
        auto_ptr<string>(new string("145125")),
    };
    auto_ptr<string> pa;
    pa = a[2];  //若使用unique_ptr 编译不通过,此处报错
    //此处将所有权从a【2】给pa,a【2】不再引用该字符串。在 auto_ptr放弃对象所有权后,便可能使用它来访问该对象,此时*a【2】为空指针
    cout << "________________________\n";
    for (auto p : a)
        cout << *p << endl;     //打印5667异常退出 ,若改用shared_ptr则不会;
                                
    cout << "the is " << *pa << endl;
}

unique_ptr优于auto_ptr

  	auto_ptr<string>p1(new string("088"));
    auto_ptr<string>p2;
    p2 = p1;   //p2接管string对象所有权后,p1的所有权将被剥夺。可以防止析构同一个对象两次,但是此时p1指向无效的数据,使用p1会出现错误

    unique_ptr<string>p1(new string("088"));
    unique_ptr<string>p2;
    p2 = p1;   //此时编译器将报错
  1. 函数中可以
unique_ptr<string> demo(const char* s)
{
    unique_ptr<string> tmp(new string(s));
    return tmp;
}
int main()
{
    unique_ptr<string> ps;
    ps = demo("okl");
    //允许demo返回临时的,然后ps接管了,ps拥有对象的所有权,临时的将很快销毁,没有机会使用无效的数据
}

总之,程序试图将一个unique_ptr赋给另一个时,如果源unique_ptr是一个临时右值,编译器允许这样;但源unique_ptr存在很长一段时间,则编译器禁止。
unique_ptr p1 (new string(“hello”));
unique_ptr ps;
ps = p1; // no 此时p1悬挂,危险。非要这样 ,不要使用智能指针
unique_ptr p2;
p2 = unique_ptr (new string(“hello”)); //ok 调用构造,临时变量很快被销毁
2. c++有一个标准库函数std::move(),能够将一个unique_ptr赋给另一个。

unique_ptr<string> demo(const char* s)
{
    unique_ptr<string> tmp(new string(s));
    return tmp;
}
int main()
{
    unique_ptr<string> ps1,ps2;
    ps1 = demo("wttkk");
    ps2 = move(ps1);
    ps1 = demo("wt");
    cout << *ps1 << endl;
    cout << *ps2 << endl;
}
  1. unique_ptr还有另一个优点,它有一个可用于数组的变体。
    auto_ptr只能和new一起使用
    unique_ptr<double []>pad(new double(5));
    注意:智能指针只适用于用new或new【】分配的内存。

选择智能指针

1.如果程序使用多个指向同一对象的指针,应选择shared_ptr.这样的情况包括:有一个指针数组,并使用一些辅助指针来标识特定的元素,如最大和最小;两个对象包含都指向第三个元素的指针;STL容器包含指针。若编译器没有shared_ptr.使用Boost库提供的shared_ptr.
2.如果程序不需要多个指向同一个对象的指针,则可以使用 unique_ptr。如果函数使用new分配内存,并返回指向该内存的地址,将其声明为unique_ptr是不错的选择。这样所有权将转让给接受返回值的 unique_ptr,而该智能指针将负责调用delete。可将 unique_ptr存到容器中,只要不调用将一个 unique_ptr复制或赋给另一个的方法或算法(如sort())。
在这里插入图片描述
在这里插入图片描述

标准模板库

STL提供了一组表示容器,迭代器,函数对象和算法的模板。容器是一个与数组类似的单元,可以存储若干值。STL容器是同质的,即存储的值的类型相同。算法是完成特定内容的处方(排序)。
迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针
函数对象是类似于函数的对象,可以是类对象或函数指针(包括函数名,因为函数名被用作指针)。STL使得能够构造各种容器(包括数组,链表,队列)和执行各种操作(搜索,排序)。
STL不是面向对象的编程,而是一种不同的编程模式——泛型编程

模板类Vector

提供【】,将对象赋给另一个对象
在这里插入图片描述

对矢量可执行的操作

1.所有STL都提供了一些基本的方法,size(),swap(),begin() ,end()返回一个表示超过容器尾的迭代器
2.迭代器是一个广义指针,让STL能够为各种不同的容器类提供统一的接口。每个容器类都定义了一个合适的迭代器,该迭代器的类型是一个名为iterator的typedef,其作用域为整个类。
3.超过结尾:是一种迭代器,指向容器最后一个元素的后面那个元素。与C风格字符串最后一个空字符类似,空字符是一个值,而它是一个指向元素的指针。超尾元素使得插入到最后一个元素前面非常方便。
4. erase()删除矢量中给定区间的元素。接受两个迭代器参数,参数定义了要删除的区间【);
5. insert()插入,第一个参数指定了新元素的插入位置,第二三个定义了插入的区间。
在这里插入图片描述

#include <iostream>
#include<string>
#include<vector>
using namespace std;
struct Review
{
    string title;
    int rating;
};
bool FillReview(Review& rr)
{
    cout << "enter book title (q to quit): ";
    getline(cin, rr.title);
    if (rr.title == "quit")
    {
        return false;
    }
    cout << "enter book rate : ";
    cin >> rr.rating;
    if (!cin)
        return false;
    while (cin.get()!= '\n')
    {
        continue;
    }
    return true;
}
void ShowReview(const Review& rr) 
{
    cout << rr.rating << "\t" << rr.title << endl;
}
int main()
{
    vector<Review> books;
    Review temp;
    while (FillReview(temp))
    {
        books.push_back(temp);
    }
    int num = books.size();
    if (num > 0)
    {
        cout << "your enter the following:\n " << "rating\tBook\n";
        for (auto x : books)
            ShowReview(x);

        cout << "iterator \n";
        vector<Review>::iterator pr;  //可以使用auto
        for (pr = books.begin(); pr != books.end(); pr++)
            ShowReview(*pr);
        vector<Review> oldist(books);  //复制构造函数
        if (num > 3)
        {
            //移除两个
            books.erase(books.begin() + 1, books.begin() + 3);
            cout << "afer erase \n";
            for (pr = books.begin(); pr != books.end(); pr++)
                ShowReview(*pr);
            //插入一个
            books.insert(books.begin(), oldist.begin() + 1, oldist.begin() + 2);
            cout << "after insert \n";
            for (pr = books.begin(); pr != books.end(); pr++)
                ShowReview(*pr);
        }
    }
}

对矢量可执行的其它操作

程序员通常要对数组执行很多操作,如搜索,排序,随机排序,矢量模板类并没有执行这些的方法,STL从更广泛的角度定义可非成员函数来执行这些操作。即不是为每个容器类定义find()函数而是定义一个适用于所有容器类的非成员函数find()。
for_each(),random_shuffle()和sort()都接受三个参数,前两个是定义容器中区间的迭代器,最后一个是指向函数的指针。(#include)
1.for_each() 可用于任何容器类
for_each(books.begin(), books.end(), ShowReview);
2.random_shuffle() 随机排列区间中的元素 ,需要容器支持随机访问,sort()也是
random_shuffle(books.begin(), books.end());
3. sort版本 两种,一种
sort(books.begin(), books.end()),按升序排列,使用内置的<运算符。如果用户自己定义的类型,需要提供operator<函数
另一种,接受三个参数
sort(books.begin(), books.end(),f);

#include <iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
struct Review
{
    string title;
    int rating;
};
bool FillReview(Review& rr)
{
    cout << "enter book title (q to quit): ";
    getline(cin, rr.title);
    if (rr.title == "quit")
    {
        return false;
    }
    cout << "enter book rate : ";
    cin >> rr.rating;
    if (!cin)
        return false;
    while (cin.get()!= '\n')
    {
        continue;
    }
    return true;
}
void ShowReview(const Review& rr) 
{
    cout << rr.rating << "\t" << rr.title << endl;
}
bool operator<(const Review& r1, const Review& r2)
{
    if (r1.title < r2.title)
        return true;
    else if (r1.title == r2.title && r1.rating < r2.rating)
        return true;
    else
        return false;
}
bool worseThan(const Review& r1, const Review& r2)
{
    if (r1.rating < r2.rating)
        return true;
    else
        return false;
}
int main()
{
    vector<Review> books;
    Review temp;
    while (FillReview(temp))
    {
        books.push_back(temp);
    }
    int num = books.size();
    if (num > 0)
    {
        cout << "your enter the following:\n " << "rating\tBook\n";
        for_each(books.begin(), books.end(), ShowReview);
        sort(books.begin(), books.end());
        cout << "after sort by title \n" << "rating\tBook\n";
        for_each(books.begin(), books.end(), ShowReview);
        sort(books.begin(), books.end(),worseThan);
        cout << "after sort by rating \n" << "rating\tBook\n";
        for_each(books.begin(), books.end(), ShowReview);
        random_shuffle(books.begin(), books.end());
        cout << "after random \n" << "rating\tBook\n";
       for_each(books.begin(), books.end(), ShowReview);      
    }
}

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

   int a[5] = { 1,2,3,4,5 };
    for (int t : a)
        cout << t << endl;
   // for_each(books.begin(), books.end(), ShowReview);
    for (auto book : books)
        ShowReview(book);
    //若要修改内容
    for (auto & book : books)
        ShowReview(book);

泛型编程

STL是一种泛型编程。面向对象编程关注的是编程的数据方面,而泛型编程关注的是算法。它们之间的共同点是抽象和创建可重用的代码。
泛型编程旨在编写独立于数据类型的代码,C++完成通用程序的工具是模板。模板使得能够按泛型定义函数或类,而STL通过通用算法更进一步。模板让这一切成为可能,但必须对元素进行详细设计。

为何使用迭代器

理解迭代器是理解STL关键所在,模板使得算法独立于存储的数据类型,而迭代器使算法独立于使用的容器类型。

//1. 在double数组中搜索特定的数值函数
//可以用模板将这种算法推广到包含==运算符的,任意类型的数组,尽管如此仍与特定的数据结构(此处数组)关联在一起
double* find_ar(double* ar, double n, const double& val)
{
    for (int i = 0; i < n; i++)
    {
        if (ar[i] == val)
            return &ar[i];
    }
    return nullptr;
}
struct  Node
{
    double item;
    Node* p_next;
};
//2. 链表 可以用模板将这种算法推广到包含==运算符的,任意类型的链表,尽管如此仍与特定的数据结构(此处链表)关联在一起
Node* find_ll(Node* head, const double& val)
{
    Node* start;
    for (start = head; start != 0; start = start->p_next)
        if (start->item == val)
            return start;
    return nullptr;
}

从实现细节看这两种算法不同,但广义上看,相同的:将值与容器中的每个值进行比较,直到找到匹配的值为止。
泛型编程旨在用同一个find函数处理数组,链表或者其它容器类型。即函数不仅独立于容器中的数据类型,而且独立于容器本身的数据结构。模板提供了存储在容器中的数据类型的通用表示,因此还需要遍历容器中的值的通用表示,迭代器正是这种通用的表示。
要实现find函数,迭代器应该具备下面的特征
1.应能够对迭代器解除引用操作,以便访问它的引用的值。若p是一个迭代器,应对*p进行定义
2.能够将一个迭代器赋给另一个迭代器,对表达式p=q定义。
3.将一个迭代器与另一个进行比较,看他们是否相等,对!= 和== 进行定义。
4.使用迭代器遍历数组中的所有元素,定义++p 和p++。

//重写后的find_ar函数
typedef double* iterator;
iterator find_ar(iterator begin, iterator end, const double& val)
{
    iterator ar;
    for (ar = begin; ar!=end; ar++)
    {
        if (*ar == val)
            return ar;
    }
    return end;
}
struct  Node
{
    double item;
    Node* p_next;
};
//重写个类
class iterator {
    Node* pt;
public:
    iterator() :pt(0) {}
    iterator(Node* pn) :pt(pn) {}
    double operator*() { return pt->item; }
    iterator& operator++()   //++int  前增
    {
        pt = pt->p_next;
        return *this;
    }
    iterator& operator++(int ) //int++  后增
    {
        iterator tmp = *this;
        pt = pt->p_next;
        return tmp;
    }
    // operator ==      operator !=
};
//重写后的函数
iterator find_ll(iterator head, const double& val)
{
    iterator start;
    for (start = head; start != 0; ++start)
        if (*start == val)
            return start;
    return nullptr;
}

重写后,两个函数基本相同。差别在于如何确定已经达到最后一个值,find_ar使用超尾迭代器,find_ll使用存储在最后一个节点中的空值。
可以要求链表的最后一个元素后面还有一个额外的元素,即让数组和链表都有超尾元素。(增加超尾元素后,对迭代器类的要求变成了对容器类的要求)
STL遵循上面的方法,每个容器类(vector,list,deque等)定义了相应的迭代器类型。对于其中的某个类,迭代器可能是指针,也可能是对象。但不管实现方式如何,都提供了 ++ * 等操作。其次每个迭代器都有一个超尾标记,当迭代器递增到超越容器的最后一个元素后,这个值将被赋给迭代器。每个容器类都有start和end方法,指向第一个元素和超尾位置的迭代器。
总结:首先处理容器类的算法,应尽可能用通用的术语来表示算法,使之独立于数据类型和容器类型。为使通用算法能够 适用于具体的情况,应定义能够满足算法需求的迭代器,并把要求加到容器设计上。即基于算法的要求,设计基本迭代器的特征和容器特征。

迭代器类型

不同算法对迭代器的要求也不同。查找算法需要定义++,要求读取数据。排序算法要求能随机访问,读写数据。
STL定义了五种迭代器类型。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

迭代器层次结构

正向迭代器具有输入和输出迭代器的全部功能,同时还有自己的功能;
双向迭代器具有正向迭代器的全部功能,同时还有自己的功能;
随机访问迭代器具有双向迭代器的全部功能,同时还有自己的功能;
在这里插入图片描述
在这里插入图片描述

概念 改进和模型

在这里插入图片描述

  1. 将指针用作迭代器
    迭代器是广义指针,而指针满足所有迭代器要求,迭代器是STL算法的接口,而指针是迭代器,因此STL算法可以使用指针来对基于指针的非STL容器进行操作。STL算法用于数组。
    (1)sort
    double a[5] = { 4,6,12,2,8};
    sort(a, a + 5); //也可以 sort(&a[0], &a[5]);
    vector va(5); // 此处最好加上大小,否则会崩溃
    copy(a, a + 5, va.begin());
    也可以将STL用于自己设计的数组形式,只要提供适当的迭代器(可以是指针,也可以是对象)和超尾指示器即可。
    (2)copy()前两个迭代器参数表示要复制的范围,最后一个迭代器参数表示要将第一个元素复制到什么位置。
    前两个参数必须是(最好是)输入迭代器,后一个最好是,必须是输出迭代器。Copy将覆盖目标容器中已有的数据,同时目标容器必须足够大,以便能够容纳被复制的元素。
    STL提供了输出流迭代器:ostream_iterator模板,头文件iterator
   double a[5] = { 4,6,12,2,8};
    sort(a, a + 5);  //也可以 sort(&a[0], &a[5]);
    vector<double> va(5);
    copy(a, a + 5, va.begin());
    ostream_iterator<double, char> out_iter(cout, ": ");
    //第一个参数double指出了被发送给输出流的数据类型,
    //第二个参数char指出了输出流使用的字符类型(也可能是wchar_t)
    //构造函数,第一个参数cout指出要使用的输出流,第二个字符串是在发送给输出流的每个数据项后显示的分隔符
  //  *out_iter++ = 15;  // ==>cout<<15<<""
    //将copy用于迭代器
    copy(va.begin(), va.end(), out_iter);
    cout << "+++++++++++++++++\n";
    copy(va.begin(), va.end(), ostream_iterator<double, char>(cout, "|"));
头文件iterator还定义了一个istream_iterator模板,可以使用两个istream_iterator对象来定义copy的输入范围
    copy(istream_iterator<int, char>(cin), istream_iterator<int, char>(), va.begin());
    //第一个参数指出了要读取的数据类型,
   //第二个参数char指出了输入流使用的字符类型(也可能是wchar_t)
   //构造函数,cin意味着使用由cin管理的输入流,省略构造函数参数,表示输入失败。
   //因此上述代码从输入流中读取,直到文件尾,类型不匹配或出现其他输入故障为止
  1. 其它有用途的迭代器
    在这里插入图片描述
#include <iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<iterator>
using namespace std;

int main()
{
    double a[5] = { 4,6,12,2,8};
    vector<double> va(5);
    copy(a, a + 5, va.begin());
    ostream_iterator<double, char> out_iter(cout, ": ");
    copy(va.begin(), va.end(), out_iter);
    cout << "\n+++++++++++++++++\n";
    copy(va.rbegin(), va.rend(), out_iter);  //反向输出
    cout << "\n+++++++++++++++++\n";
    vector<double>::reverse_iterator ri;     //反向输出
    for (ri = va.rbegin(); ri != va.crend(); ++ri) 
        cout << *ri << endl;
}

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<iterator>
using namespace std;
void output(const std::string& s) { cout << s << " "; }
int main()
{
    string s1[4] = { "012","321", "956", "753" };
    string s2[2] = { "123","hello" };
    string s3[2] = { "456","789"};
    vector<string> words(4);
    copy(s1, s1 + 4, words.begin());
    for_each(words.begin(),words.end(), output);
    cout << "\n+++++++++++++++++++++++++++\n";
    copy(s2, s2 + 2, back_insert_iterator<vector<string> >(words));
    for_each(words.begin(), words.end(), output);
    cout << "\n+++++++++++++++++++++++++++\n";
    copy(s3, s3 + 2, insert_iterator<vector<string> >(words,words.begin()));
    for_each(words.begin(), words.end(), output);
}

输出:
012 321 956 753
+++++++++++++++++++++++++++
012 321 956 753 123 hello
+++++++++++++++++++++++++++
456 789 012 321 956 753 123 hello

容器种类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include<string>
#include<list>
#include<algorithm>
using namespace std;
void outint(int n) { cout << n << " "; }
int main()
{
	list<int> l1(5, 2);  //5个2
	int stuff[5] = { 1,2,3,4,5 };
	list<int> l2;
	l2.insert(l2.begin(), stuff, stuff + 5);
	int more[6] = { 6,2,4,4,5,6 };
	list<int> l3(l2);
	l3.insert(l3.begin(), more, more + 6); 

	cout << " list 1\n";
	for_each(l1.begin(), l1.end(), outint);  //2  2 2 2 2
	cout << "\n list 2\n";
	for_each(l2.begin(), l2.end(), outint);  // 1 2 3  4 5
	cout << "\n list 3\n";
	for_each(l3.begin(), l3.end(), outint); // 6,2,4,4,5,6  1 2 3  4 5
	l3.remove(2);  //移除所有2
	cout << "\n list 3 remove\n";
	for_each(l3.begin(), l3.end(), outint);   //6 4 4 5 6 1 3 4 5
	l3.splice(l3.begin(), l1); 
	cout << "\n list 3 splice\n";
	for_each(l3.begin(), l3.end(), outint); //2 2 2 2 2 6 4 4 5 6 1 3 4 5
	cout << "\n list 1\n";
	for_each(l1.begin(), l1.end(), outint);   //为空 
	//insert 和splice的区别,insert将原始区间的副本插入目标地址。
	//splice将原始区间移到目标地址,之后原始区间为空
	//splice方法执行后,迭代器仍然有效。也就是说,如果将迭代器设置为指向l1中的元素
	//则在splice将它重新定位到元素l3后,该迭代器仍指向相同的元素
	l3.unique();   //unique只能将相邻的相同值压缩为单个值。但在sort之后在使用unique便可以
	cout << "\n list 3 unique\n";
	for_each(l3.begin(), l3.end(), outint);  //2 6 4 5 6 1 3 4 5
	l3.sort();
	l3.unique();  
	cout << "\n list 3 unique and sort\n";
	for_each(l3.begin(), l3.end(), outint);  //1 2 3 4 5 6 

	l2.sort();   //12345
	l3.merge(l2);
	cout << "\n sort two merage into three\n";
	for_each(l3.begin(), l3.end(), outint);   //1 1 2 2 3 3 4 4 5 5 6
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

关联容器

关联容器是对容器概念的另一个改进。关联容器将值与键关联在一起,并使用键来查找值。(采用树来实现)
优点:提供了对元素的快速访问(比链表还快)。允许插入新元素,但不允许指定位置插入。原因是关联容器通常有用于确定数据放置位置的算法,以便能够快速检索信息
STL提供了四种关联容器set multiset map multimap 头文件set和map
1.set 值类型与键相同,键是唯一。multiset类似set但可以有多个值的键相同
2.map值和键类型不同,键是唯一的,每个键只能对应一个值。multimap可以一键多值

  1. set 示例
    在这里插入图片描述
    在这里插入图片描述
// pr.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include<string>
#include<list>
#include<set>
#include<iterator>
#include<algorithm>
using namespace std;

int main()
{
	const int N = 6;
	string s1[N] = { "for","heelo","for","hello", "for","world" };
	string s2[N] = { "12r","for","123","456","678","89" };
	set<string> A(s1, s1 + N);
	set<string> B(s2, s2 + N);

	ostream_iterator<string, char>out(cout, " ");
	cout << "Set A:";
	copy(A.begin(), A.end(), out);    //for heelo hello world
	cout << endl;
	cout << "Set B:";
	copy(B.begin(), B.end(), out);   //123 12r 456 678 89 for
	cout << endl;
	cout << "union of A and B \n";   //并集
	set_union(A.begin(), A.end(), B.begin(), B.end(), out); //123 12r 456 678 89 for heelo hello world
	cout << endl;

	cout << "interface of A and B \n";   //交集
	set_intersection(A.begin(), A.end(), B.begin(), B.end(), out); //for 
	cout << endl;
	cout << "difference of A and B \n";   //集合的差
	set_difference(A.begin(), A.end(), B.begin(), B.end(), out); //heelo hello world
	cout << endl;

	set<string> C;
	cout << "Set C: \n";
	set_union(A.begin(), A.end(), B.begin(), B.end(), insert_iterator<set<string> >(C,C.begin()));
	copy(C.begin(), C.end(), out); //123 12r 456 678 89 for heelo hello world
	cout << endl;

	string s3("grungy");
	C.insert(s3);
	cout << "Set C after insert: \n";
	copy(C.begin(), C.end(), out); //123 12r 456 678 89 for grungy heelo hello world
	cout << endl;

	cout << "show a range:\n";
	copy(C.lower_bound("ghost"), C.upper_bound("speak"), out);//grungy heelo hello
	cout << endl;
}

  1. map示例
    在这里插入图片描述
    在这里插入图片描述
#include <iostream>
#include<string>
#include<list>
#include<map>
#include<iterator>
#include<algorithm>
using namespace std;
typedef int KeyType;
typedef std::pair<const KeyType, string> Pair;
typedef multimap<KeyType, string>MapCode;
int main()
{
	MapCode codes;
	codes.insert(Pair(415, "San"));
	codes.insert(Pair(510, "Tan"));
	codes.insert(Pair(718, "Aan"));
	codes.insert(Pair(718, "gan"));
	codes.insert(Pair(415, "tan"));
	codes.insert(Pair(510, "ban"));
	cout << " Number of citys with area code 415 :" << codes.count(415) << endl;
	cout << " Number of citys with area code 510 :" << codes.count(510) << endl;
	cout << " Number of citys with area code 718 :" << codes.count(718) << endl;

	cout << "Area Code city\n";
	MapCode::iterator it;
	for (it = codes.begin(); it != codes.end(); ++it)
		cout << " " << (*it).first << "  " << (*it).second << endl;

	cout << "==================================\n";
	pair<MapCode::iterator, MapCode::iterator> range = codes.equal_range(718);
	cout << "code is 718\n";
	for (it = range.first; it != range.second; ++it)
		cout << (*it).second << endl;
}

无序列关联容器C++11

在这里插入图片描述

函数对象

很多STL算法都使用函数对象,也叫函数符。函数符可以以函数方式与()结合使用的任何对象。这包括函数名,指向函数的指针和重载了()运算符的对象(即定义了函数operator()() )

class Liner {
private:
    double slope;
    double y0;
public:
    Liner(double s = 1, double y = 0) :slope(s), y0(y) {}
    double operator()(double x) { return y0 + slope * x; }
};
//重载的()运算符使得能够像函数那样使用Liner对象
Liner f1;
Liner f2(2.5, 10.0);
double y1 = f1(12.5);    //0+1*12.5
double y2 = f2(0.4);     //10+ 0.4 * 2.5

for_each()函数的第三个参数可以是常规函数,也可以是函数符。如何声明第三个参数

函数符概念

正如STL定义了容器和迭代器的概念一样,也定义了函数符的概念
1.生成器是不用参数就可以调用的函数符
2.一元函数是用一个参数可以调用的函数符
3.二元函数是用两个参数可以调用的函数符
在这里插入图片描述
在这里插入图片描述


```cpp
#include <iostream>
#include<list>
#include<iterator>
#include<algorithm>
using namespace std;
template<class T>
class TooBig
{
private:
    T cutoff;
public:
    TooBig(const T& t) :cutoff(t) {}
    bool operator()(const T& v) { return v > cutoff; }
};
void outint(int n) { cout << n << " "; }
int main()
{
    std::cout << "Hello World!\n";
    TooBig<int> f100(100);
    int vals[10] = { 50,100,90,180,60,210,415,88,188,201 };
    list<int>yadayada(vals, vals + 10);
    list<int>etcetera(vals, vals + 10);
    //c++11  list<int> a{1,2,3,5};
    cout << "orign list\n";
    for_each(yadayada.begin(), yadayada.end(), outint);
    cout << endl;
    for_each(etcetera.begin(), etcetera.end(), outint);
    cout << endl;
    yadayada.remove_if(f100);  // 返回true则删除  移除大于100的
    etcetera.remove_if(TooBig<int>(200));//移除大于200的
    cout << "after trimm\n";
    for_each(yadayada.begin(), yadayada.end(), outint);
    cout << endl;
    for_each(etcetera.begin(), etcetera.end(), outint);
    cout << endl;
}

在这里插入图片描述
在这里插入图片描述

预定义函数符

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

自适应函数符和函数适配器

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include<vector>
#include<iterator>
#include<algorithm>
#include<functional>
using namespace std;
const int LIM = 6;
void Show(double v)
{
    cout.width(6);
    cout << v << ' ';
}
int main()
{
    double a1[LIM] = { 28,29,30,35,38,59 };
    double a2[LIM] = { 63,39,36,38,37,69 };
    vector<double> gr8(a1, a1 + LIM);
    vector<double> m8(a1, a1 + LIM);
    cout.setf(ios_base::fixed);
    cout.precision(1);
    cout << "g8 \t";
    for_each(gr8.begin(), gr8.end(), Show);
    cout << endl;
    cout << "m8 \t";
    for_each(m8.begin(), m8.end(), Show);
    cout << endl;

    vector<double> sum(LIM);
    transform(gr8.begin(), gr8.end(), m8.begin(), sum.begin(), plus<double>());
    cout << "sum \t";
    for_each(sum.begin(), sum.end(), Show);
    cout << endl;

    vector<double> prod(LIM);
    transform(gr8.begin(), gr8.end(), prod.begin(), bind1st(multiplies<double>(), 2.5));
    cout << "Prd \t";
    for_each(prod.begin(), prod.end(), Show);
    cout << endl;
}

注意:C++11提供了函数指针和函数符的替代品----lambda表达式

算法

在这里插入图片描述

算法组

在这里插入图片描述
在这里插入图片描述

算法的通用特征

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

STL和string类

string类虽然不是STL的组成部分,但设计它时考虑到了STL(包含begin,end等方法)。next_permutation()算法将区间内容转换为下一种排序方式。对于字符串排列按照字母递增的顺序进行。如果成功返回true。如果区间已经处于最后的序列中,则返回false。(因此使用之前应先将串排序)

    string str("asda");
    sort(str.begin(), str.end());  
    cout << str << endl;
    while (next_permutation(str.begin(), str.end()))
    {
        cout << str << endl;
    }

函数和容器方法

有时可以选择使用容器方法或者STL函数,通常使用容器方法是最好的选择。首先它更适用于特定的容器;其次作为成员函数,它可以使用模板类的内存管理工具,在需要时调整容器的长度。
在这里插入图片描述

#include <iostream>
#include<list>
#include<iterator>
#include<algorithm>
using namespace std;
const int LIM = 10;
void Show(int v)
{
    cout << v << ' ';
}
int main()
{
    int ar[LIM] = { 28,29,30,35,38,59,28,30,28,40};
    list<int>la(ar, ar + LIM);
    list<int>lb(la);
    cout << "original list \n";
    for_each(la.begin(), la.end(), Show);
    cout << endl;
 
    la.remove(28);
    cout << "la after remove 28 \n";
    for_each(la.begin(), la.end(), Show);  //包含7个元素
    cout << endl;
    list<int>::iterator last;
    last = remove(lb.begin(), lb.end(), 28); //包含10个元素,前面7个非28
    cout << "lb after remove 28 \n";
    for_each(lb.begin(), lb.end(), Show);
    cout << endl;

    lb.erase(last, lb.end());
    cout << "lb after erase \n";
    for_each(lb.begin(), lb.end(), Show);  //与调用容器类的remove后结果相同
    cout << endl;
}

使用STL

使用STL时应尽可能减少编写的代码。STL通用,灵活的设计将节省大量工作。
在这里插入图片描述

在这里插入图片描述

#include <iostream>
#include<list>
#include<map>
#include<set>
#include<cctype>
#include<iterator>
#include<algorithm>
#include<vector>
using namespace std;
char toLower(char ch) { return tolower(ch); }
string& ToLower(string& st) 
{
    transform(st.begin(), st.end(), st.begin(), toLower);
    return st;
}
void diplay(const string &s)
{
    cout << s << " ";
}
int main()
{
    vector<string> words;
    cout << "enter words (enter quit to quit):\n";
    string input;
    while (cin>>input&&input!="quit")
    {
        words.push_back(input);
    }
    cout << "your enter is :\n";
    for_each(words.begin(), words.end(), diplay);
    cout << endl;
    //转小写  使用sort-》unique但这么做会覆盖原有的数据
    set<string>wordset;
    transform(words.begin(), words.end(), insert_iterator<set<string> >(wordset, wordset.begin()), ToLower);
    cout << "after low is :\n";
    for_each(words.begin(), words.end(), diplay);
    cout << endl;
    
    map<string, int>wordmap;
    set<string>::iterator si;
    for (si = wordset.begin(); si != wordset.end(); si++)
        wordmap[*si] = count(words.begin(), words.end(), *si);
    cout << "Result\n ";
    for (si = wordset.begin(); si != wordset.end(); si++)
        cout << *si << ":" << wordmap[*si] << endl;
}

其它库

C++还提供了一些其它库。头文件complex为复数提供了类模板complex,包含用于float,long和long double 的具体化,这个类提供了标准的复数运算以及能够处理复数的标准函数
C++11新增的头文件random提供了更多的随机数功能。

vector valarray array

vector valarray array三个数组模板,由不同的小组开发的用于不同的目的
1.vector模板类是一个容器类和算法系统的一部分,它支持面向容器的操作。如排序,插入,重新排列,搜索,将数据转移到其它容器中等。
2.valarray类模板是面向数值计算的,不是STL的一部分,没有push_back()和insert()等方法,但为数学运算提供了一个简单,直观的接口。
3.array是为替代内置数组而设计的,它通过提供更好,更安全的接口,让数组更加紧凑,效率更高。Array表示长度固定的数组,因此不支持push_back()和insert(),但提供了多个STL方法,如begin(),end(),rbegin(),rend(),这使得很容易将STL算法用于array对象。
4.若要将两个数组中第一个元素的和赋给第三个数组中的第一个元素

#include <iostream>
#include<list>
#include<map>
#include<set>
#include<cctype>
#include<iterator>
#include<algorithm>
#include<vector>
#include<valarray>
#include<functional>
using namespace std;
void diplay(double s)
{
    cout << s << " ";
}
int main()
{
    vector<double> vd1{ 1,2,3 };
    vector<double> vd2{ 3,4,5 };
    vector<double> vd3(3);
    //使用vector
    transform(vd1.begin(), vd1.end(), vd2.begin(), vd3.begin(), plus<double>());
    for_each(vd3.begin(), vd3.end(), diplay);
    //将数组元素扩大2.5
    transform(vd3.begin(), vd3.end(), vd3.begin(),bind1st(multiplies<double>(),2.5));
    for_each(vd3.begin(), vd3.end(), diplay);
    //计算自然数对数,存在另一个数组相应的元素中
    //transform(vd1.begin(), vd1.end(), vd3.begin(), log);

    //array同上
    valarray<double> v1{ 1,2,3 };
    valarray<double> v2{ 3,4,5 };
    valarray<double> v3 = v1 + v2;  //valarray类重载了 +  和*
    valarray<double> v4 = v3*2.5;
    v3 = log(v1);//valarray类重载 log函数
    v3 = v1.apply(log);  //也可以,不修改调用对象,而是返回一个包含结果的新对象
    cout << "\n++++++++++++++++++++\n";
    for (double x : v3)
        cout << x << " ";
    //可以
    sort(&v1[0], &v1[3]); // 但危险,越界,valarray类没有超尾元素
    //c++11提供接受valarray对象作为参数的模板函数,begin和end
    sort(begin(v1), end(v1));
}
#include <iostream>
#include<list>
#include<map>
#include<set>
#include<cctype>
#include<iterator>
#include<algorithm>
#include<vector>
#include<valarray>
#include<functional>
using namespace std;
int main()
{
    vector<double> data;
    double temp;
    cout << "Enter numbers (<=0 to quit)\n";
    while (cin >> temp && temp > 0)
    {
        data.push_back(temp);
    }
    sort(data.begin(),data.end());
    int size = data.size();
    valarray<double>numbers(size);
    int i;
    for (i = 0; i < size; i++)
        numbers[i] = data[i];
    valarray<double>sq_rts(size);
    sq_rts = sqrt(numbers);
    valarray<double> results(size);
    results = numbers + 2.0 * sq_rts;
    cout.setf(ios_base::fixed);
    cout.precision(4);
    for (int i = 0; i < size; i++)
    {
        cout.width(8);
        cout << numbers[i] << ":";
        cout.width(8);
        cout << results[i] << endl;
    }
}

###

#include <iostream>
#include<cstdlib>
#include<valarray>
using namespace std;
const int SIZE = 12;
typedef valarray<int> vint;
void Show(const vint& v, int cols)
{
	int lim = v.size();
	for (int i = 0; i < lim; ++i)
	{
		cout.width(3);
		cout << v[i];
		if (i % cols == cols - 1)
			cout << endl;
		else
			cout << " ";
	}
	if (lim % cols != 0)
		cout << endl;
}
int main()
{
	vint valint(SIZE);  // 4*3
	int i;
	for (i = 0; i < SIZE; i++)
		valint[i] = rand() % 10;
	cout << "orign \n";
	Show(valint, 3);
	vint vcol(valint[slice(1, 4, 3)]);
	cout << "seconde col\n";
	Show(vcol, 1);
	vint vRow(valint[slice(3, 3, 1)]);
	cout << "seconde vRow\n";
	Show(vRow, 3);

	valint[slice(2, 4, 3)] = 10;
	cout << "Set last  col\n";
	Show(valint, 3);
	valint[slice(0, 4, 3)] = vint{ valint[slice(1, 4, 3)] } +
		vint{ valint[slice(2, 4, 3)] };
	cout << "addd \n";
	Show(valint, 3);
}

模板initializer_list(C++11)

模板initializer_list C++11新增的,可使用初始化列表语法将STL容器初始化为一系列值。
在这里插入图片描述
在这里插入图片描述

使用initializer_list

要在代码中使用initializer_list,需要头文件initializer_list,这个模板类包含成员函数begin和end。可以使用这些函数来访问列表元素。它还包含成员函数size()。

#include <iostream>
#include<initializer_list>
using namespace std;
//可以按值传递,也可以按引用传递。这种对象本身很小,
//通常是两个指针,一个指向开头,一个指向结尾,传递方式不会影响
//函数参数可以是initializer_list 变量,也可以是initializer_list 字面量{2,3,4}
double sum(initializer_list<double> il)
{
	double tot = 0;
	for (auto p = il.begin(); p != il.end(); p++)
		tot += *p;
	return tot;
}
double average(const initializer_list<double>& ril)
{
	double total = 0;
	int n = ril.size();
	double ave = 0;
	if (n > 0)
	{
		for (auto p = ril.begin(); p != ril.end(); p++)
			total += *p;
		ave = total / n;
	};
	return ave;
}

int main()
{
	cout << "list 1:sum = " << sum({ 2,3,4 }) << ",ave = " << average({ 2,3,4 }) << endl;
	initializer_list<double> d1 = { 1.1,1.2,1.3,1.4,1.5 };
	cout << "list 2:sum = " << sum(d1) << ",ave = " << average(d1) << endl;
}

提供initializer_list让能够将一系列值传递给构造函数或者其它函数

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值