你看自身就够了

STL(规范模板库),是方今C++内置扶助的library。它的底层利用了C++类模板和函数模板的编写制定,由三大学一年级部分组成:容器、算法和迭代器。

1.STL

此时此刻STL有六大组件

  [1]STL容器是直接管理存款和储蓄数据。

  • 容器 container
  • 算法 algorthm
  • 迭代器 iterator
  • 仿函数 function object
  • 适配器 adaptor
  • 空中配置器 allocator

  [2]STL适配器是在那几个器皿下边扩大一些操作函数,封装成三个模板。

上面,大家会挨个实行介绍。

2.STL 容器

STL初探

容器是STL中很入眼的一种数据结构。常见的器皿包含

  • vector容器
  • deque双端数组
  • stack栈模型
  • queue队列模型
  • list链表模型
  • priotriy_queue优先级队列
  • set与multiset容器
  • map与multimap容器

除去容器,STL还包裹了强硬的算法,可以达成查找、删除、替换、删除等相当多相近操作。前边会器重教学。

别的,迭代器也是STL主要的一环,通过迭代器,大家能够很平价对容器中的成分举行遍历,以及操作容器。前面我们会穿插批注迭代器。

  [1]容器类型:

STL中的string

string是STL的字符串类型,在C语言中,大家一般用char *或者char[]字符数组来代表字符串。C++的string和C语言的char *有怎么样不相同吗?

  • string是一个类,char *是指向字符的指针
  • string封装了char *,管理这么些字符串,是四个char *品种的器皿
  • string毫不思量内部存款和储蓄器释放和数组越界
  • string提供了有的列的字符串操作函数

    1)array:数组。

string的构造函数

既然string是一个类,那么也就有构造函数,大家研讨下string的构造函数。

#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {

    //通过const char * 初始化
    string s1 = "aaaa";

    //构造函数初始化
    string s2("bbbbb");

    //通过拷贝构造函数来初始化对象s3
    string s3 = s2;

    //用10个'a'字符来初始化字符串
    string s4(10, 'a');

    return 0;
}

    2)bitset:二进制位操作。

字符串的遍历

字符串的遍历,有两种遍历的办法

  • 数组格局遍历,通过[]操作符遍历 (不会抛出非常)
  • at()方法遍历,遵照index取值 (会抛出卓殊)
  • 透过STL迭代器遍历

int main(int argc, const char * argv[]) {

    //创建字符串对象
    string str("abcdefg");

    //数组形式遍历
    for (int i = 0; i < str.length(); i++) {
        cout<< str[i] << endl;
    }

    //at方法遍历
    for (int i = 0; i < str.length(); i++) {
        cout << str.at(i) << endl;
    }

    //迭代器遍历
    for (string::iterator it = str.begin(); it != str.end(); it++) {
        cout << *it << endl;
    }

    return 0;
}

数组格局和at方法格局,有贰个明显的分化

  • 数组形式,若是现身越界也许另外错误,不会抛出万分,程序直接终端。
  • at()方法遍历,出现越界或别的错误,会抛出非凡,程序能够拍卖非常。

迭代器其实能够当作是四个字符的指针,上个例子中string::iterator it = str.begin()便是概念一个string类型的迭代器,指向str的首先次地方。*it就意味着近日的字符。注意str.end()代表字符串最终二个字符的前边一个岗位。若是it == str.end()就象征曾经遍历到终点了。

    3)deque:双向队列。

string与char *的转换

string提供了成员函数c_str来将string对象转化成const char *。string提供了copy(buf,size,begin)分子函数来说string从begin岗位上马的size个字符拷贝到buf中。要求小心的是:

  • 若果buf容纳不下,会越界
  • 拷贝过去后,不会变动成C风格的字符串,也等于不会在buf前边增添’\0′

int main(int argc, const char * argv[]) {

    //1 string转char *
    string str("aaaaaa");
    const char *p = str.c_str();

    //2 char *转string
    const char *p1 = "12345";
    string str2 = p1;

    //3 string拷贝到buf[]中
    char buf[128] = {0};
    //从0开始,拷贝3个字符到buf指向的内存空间
    //如果buf空间容纳不下,会越界
    //拷贝过去时,不会给buf末尾添加\0
    str.copy(buf, 3, 0);

    return 0;
}

    4)forward_list:单向链表。

string的拼接

string为我们提供了三种字符串拼接格局,一种是重写了 +
操作符,大家能够直接将连个字符串相加,类似于java的语法。另一种是string提供了成员函数append()供大家拼三回九转个字符串.

int main(int argc, const char * argv[]) {

    string s1 = "123456";
    string s2 = "abcdef";

    //直接使用加号运算符拼接
    string s3 = s1 + s2;

    //使用成员函数拼接
    string s4 = s1.append(s2);

    cout<<s3<<endl;
    cout<<s4<<endl;

    return 0;
}

    5)list:双向链表。

string的查找和替换

string类提供了find函数,用来查找字符串中钦赐的字符。提供了replace函数,用来替换字符串中钦定地方的字符串。

replace函数是,先删除钦赐地点,钦点长度的字符,然后在当下钦定地方插入新的字符。

int main(int argc, const char * argv[]) {


    string s1 = "hello hello hello hello hello hello 1234 7876";

    //从0位置开始查找第一个hello出现的首位位置
    size_t index1 = s1.find("hello",0);
    cout << index1 << endl;

    //查找第一个hello出现时的首位位置
    size_t index2 = s1.find_first_of("hello");
    cout << index2 << endl;

    //查找最后一个hello出现时的末尾位置
    size_t index3 = s1.find_last_of("hello");
    cout << index3 << endl;

    //求hello出现的次数,以及对应的下标
    int count = 0;
    size_t offindex = s1.find("hello",0);
    while (offindex != string::npos) {  //如果 offindex != -1
        //找到了
        cout << "索引:" << offindex <<endl;
        count++;
        offindex++;
        offindex = s1.find("hello", offindex);
    }

    //把hello替换成welcome
    size_t offindex1 = s1.find("hello", 0);
    while (offindex1 != string::npos) {

        //从offindex1的位置开始删除5个位置,并插入新的字符串welcome
        s1.replace(offindex1, strlen("hello"), "welcome");

        //从后面的位置开始
        offindex1 += strlen("welcome");

        //继续查找
        offindex1 = s1.find("hello", offindex1);
    }
    cout << "替换后的字符串:" << s1 <<endl;

    return 0;
}

    6)map(multimap):map:键值非重复容器,multimap:键值可另行容器。 

string区间删除和插入

string提供了inserterase独家达成插入和删除操作。

插入:pos地方插入字符串s,重临新的string。

  • string &insert(int pos, const char *s)
  • string &insert(int pos, const string &s)

插入:pos地方插入n个字符c,再次来到string。

  • string &insert(int pos, int n, char c)

剔除:删除从pos地点上马的n个字符,再次回到新的string

  • string &erase(int pos, int n)

删去:删除内定迭代器的职责,重回当前迭代器地方

  • string::iterator erase(string::iterator it)

去除:删除迭代器之间的字符,左闭右开区间

  • string::iterator erase(string::iterator beginIt, string::iterator endIt)

int main(int argc, const char * argv[]) {

    string s1 = "hello1 world!";

    //1 删除字符串中的'1'
    //---通过find函数,查找'1'所在的迭代器位置
    string::iterator it = find(s1.begin(), s1.end(), '1');
    //---删除
    if (it != s1.end()) {
        s1.erase(it);
    }
    cout << s1 << endl;

    //2 删除起始迭代器位置的字符
    s1.erase(s1.begin(), s1.begin() + 3);
    cout << s1 << endl;

    //3 在0位置插入"AAA"
    s1.insert(0, "AAA");
    cout << s1 << endl;

    return 0;
}

    7)set(multiset):自动排序非重复集结。

string算法相关

近来大范围的string的算法是高低写转变。一般选择函数transform来开展转移。

int main(int argc, const char * argv[]) {

    string s1 = "abcdefg";
    string s2 = "AEDLFLKJDLKJFL";

    //小写全部转换成大写,转换的结果放在s1.begin()的位置,后面的操作需要强制转换成指定的函数类型
    transform(s1.begin(), s1.end(), s1.begin(), (int (*)(int))toupper);
    cout << s1 <<endl;

    //大写全部转换成小写
    transform(s2.begin(), s2.end(), s2.begin(), (int (*)(int))tolower);
    cout << s2 <<endl;

    return 0;
}

    8)vector:动态数组。

STL中的vector容器

vector是将成分放到动态数组中加以管理的器皿。vector容器可以随机存取成分,也正是说协理[]运算符和at方法存取。

  • vector在尾巴部分增加也许移除成分非常快,在中游操作特别耗费时间,因为它供给活动成分

    9)unordered_set(unordered_multiset):依照hash来囤积的set(multiset)。

vector的基本用法

既然如此vector是容器,那么就能够向这么些容器增多删减成分。

着力用法:

  • front()回到底部成分的援引,能够当左值
  • back()回来后面部分成分的援引,能够当左值
  • push_back()添港成分,只好尾巴部分增加
  • pop_back()移除成分,只可以在尾巴部分移除

int main(int argc, const char * argv[]) {

    //定义一个vector容器
    vector<int> v1;

    //插入元素(尾部插入)
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);

    //迭代器遍历打印
    for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;

    //修改头部元素的值(front()返回是引用,可以当左值)
    v1.front() = 44;

    //输出头部元素
    cout<< "头部元素:" << v1.front() << endl;

    //修改尾部的值(back()返回是引用,可以当左值)
    v1.back() = 99;

    //输出尾部元素
    cout << "尾部元素" << v1.back() <<endl;

    //删除元素(从尾部删除)
    v1.pop_back();

    //迭代器遍历打印
    for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;

    return 0;
}

    10)unordered_map(unordered_multimap):根据hash来存款和储蓄的map(multiset)。

vector的起先化

vector有4种方法初步化,有一贯初叶化,也要经过拷贝构造函数初叶化。

int main(int argc, const char * argv[]) {

    //直接构造函数初始化
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);

    //通过拷贝构造函数初始化
    vector<int> v2 = v1;

    //使用部分元素来构造
    vector<int> v3(v1.begin(), v1.begin() + 1);
    vector<int> v4(v1.begin(), v1.end());

    //存放三个元素,每个元素都是9
    vector<int> v5(3,9);

    return 0;
}

  [2]容器的归类:

vector的遍历

vector的遍历有三种方法,能够依照[]要么迭代器遍历。

急需注重的是:

  • []方法,若是越界或出现其余错误,不会抛出万分,大概会崩溃,或者数量随机出现
  • at艺术,假若越界或出现任何错误,会抛出非常,必要捕获极度并拍卖
  • 迭代器提供了逆向遍历,能够经过迭代器来达成逆向遍历,当然上边二种艺术也足以

int main(int argc, const char * argv[]) {

    //创建vector
    vector<int> v1;

    //插入元素
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
    }

    //遍历-[]取值
    for (int i = 0; i < v1.size(); i++) {
        cout << v1[i] << " ";
    }
    cout << endl;

    //遍历-at取值
    for (int i = 0; i < v1.size(); i++) {
        cout << v1.at(i) << " ";
    }
    cout << endl;

    //遍历-迭代器遍历
    for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;

    //遍历-迭代器逆向遍历
    for (vector<int>::reverse_iterator it = v1.rbegin(); it != v1.rend(); it++) {
        cout << *it << " ";
    }
    cout << endl;

    //测试越界
    cout << "[]越界:" << v1[20] << endl;      //不会抛出异常,可能会崩溃,可能会乱码
    cout << "at越界:" << v1.at(20) << endl;   //会抛出异常,需要捕获异常

    return 0;
}

    1)容器分为顺序容器和涉嫌容器。

vector的push_back强化

push_back是在眼下vector的内部存款和储蓄器末尾拷贝元素进入容器。注意这一个地点大概发生浅拷贝,所以容器中的对象要援助拷贝操作。另外,假若vector伊始化了个数,而不最早化具体的值,push_back也只会在终极面追加。

int main(int argc, const char * argv[]) {

    //初始化10个元素的容器
    vector<int> v(10);

    //打印容器大小
    cout << v.size() << endl;

    //push_back添加元素
    v.push_back(100);

    //打印容器大小
    cout << v.size() << endl;

    //遍历后的结果是  0 0 0 0 0 0 0 0 0 0 100
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;

    return 0;
}

    2)顺序容器是不举办机动排序的,关联容器是安分守己成分的值进行活动排序的。在那之中map和set是涉及容器,其余的是逐个容器,最重视的便是关联容器有find方法,可以飞速寻找成分。

vector的成分删除

vector的删减,是基于岗位进行删除,如果想要删除有些成分,须要找到当前成分的迭代器地点,再实行删除。

erase(iterator)函数,删除后会重回当前迭代器的下三个职责。

int main(int argc, const char * argv[]) {

    //1 创建容器并初始化
    vector<int> v1(10);
    for (int i = 0; i < v1.size(); i++) {
        v1[i] = i;
    }

    //2 区间删除
    //--2.1 删除前3个元素
    v1.erase(v1.begin(), v1.begin() + 3);

    //--2.2 删除指定位置的元素
    v1.erase(v1.begin() +3);

    //3 根据元素的值进行删除,删除值为2的元素
    v1.push_back(2);
    v1.push_back(2);
    vector<int>::iterator it = v1.begin();
    while (it != v1.end()) {
        if (*it == 2) {
            it = v1.erase(it);   //删除后,迭代器指针会执行下一个位置并返回。
        }else{
            it++;
        }
    }

    //4 遍历打印
    for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;

    return 0;
}

  [3]各类容器的运用验证:

vector的插入成分

vector提供了insert函数,结合迭代器地点插入钦赐的成分。

一旦迭代器地点越界,会抛出极度。

int main(int argc, const char * argv[]) {

    //初始化vector对象
    vector<int> v1(10);

    //在指定的位置插入元素10的拷贝
    v1.insert(v1.begin() + 3, 10);

    //在指定的位置插入3个元素11的拷贝
    v1.insert(v1.begin(), 3, 11);

    //遍历
    for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;

    return 0;
}

    1)除了array是定位大小外,其余容器都提供自动内存管理。

STL中的deque容器

deque是一个双端数组容器:能够在头顶和尾巴操作成分。

  • push_back 从尾巴部分插入元素
  • push_front 从尾部插入成分
  • pop_back 从尾巴部分删除元素
  • pop_front 从底部删除元素

知识点:

distance函数能够求出当前的迭代器指针it距离尾部的岗位,也正是容器的指针

用法: distance(v1.begin(), it)

int main(int argc, const char * argv[]) {

    //定义deque对象
    deque<int> d1;

    //尾部插入元素
    d1.push_back(10);
    d1.push_back(20);
    d1.push_back(30);

    //头部插入元素
    d1.push_front(1);
    d1.push_front(2);
    d1.push_front(3);

    //尾部删除元素
    d1.pop_back();

    //头部删除元素
    d1.pop_front();

    //修改头部和尾部的值
    d1.front() = 111;
    d1.back()  = 222;

    //查找元素为1的下标
    //通过distance求取下标
    deque<int>::iterator it = d1.begin();
    while (it != d1.end()) {
        if (*it == 1) {
            cout << "下标:" << distance(d1.begin(), it) << endl;
        }
        it++;
    }

    //遍历
    for (deque<int>::iterator it = d1.begin(); it != d1.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;

    return 0;
}

    2)string和vector保存在一而再内部存款和储蓄器中,由此随机访谈相当慢,可是在在这之中插入删除会比很慢。

STL中的stack栈容器

在数据结构中,栈是一种先入后出的容器,增日币素叫压栈或然入栈。移除元素平常称为出栈。

STL提供的stack容器,也是这种基本类型。这里我们演示一下着力要素类型和参差不齐因素类型。

▽ 基础数据类型的stack

int main(int argc, const char * argv[]) {

    //定义stack对象
    stack<int> s1;

    //入栈
    s1.push(1);
    s1.push(2);
    s1.push(3);
    s1.push(4);

    //打印栈顶元素,并出栈
    while (!s1.empty()) {
        //取出栈顶元素
        cout << "当前栈顶元素" << s1.top() << endl;

        //获取栈的大小
        cout << "当前栈的大小" << s1.size() << endl;

        //出栈
        s1.pop();
    }

    return 0;
}

▽ 复杂数据类型的stack

//定义类
class Teacher {

public:

    char name[32];
    int  age;

    void printT()
    {
        cout << "age = " << age << endl;
    }

};

int main(int argc, const char * argv[]) {

    Teacher t1, t2, t3;
    t1.age = 22;
    t2.age = 33;
    t3.age = 44;

    //定义栈容器
    stack<Teacher> s1;

    //入栈
    s1.push(t1);
    s1.push(t2);
    s1.push(t3);

    //出栈并打印
    while (!s1.empty()) {
        //打印栈顶元素
        Teacher tmp = s1.top();
        tmp.printT();

        //出栈
        s1.pop();
    }

    return 0;
}

    3)deque随机访谈异常快,而且在双方插入删除异常的快,可是在中游插入删除会异常慢。

STL中的queue队列容器

队列是一种数据结构,具备队头和队尾。常见的有FIFO(先入先出)队列等。

#include <queue>

void main()
{
    queue<int> q;
    q.push(1);
    q.push(2);
    q.push(3);

    cout << "对头元素" << q.front() <<endl;
    cout << "队列的大小 " << q.size() <<endl;

    while (!q.empty())�{

        int tmp = q.front();
        cout << tmp << " ";
        q.pop();
    }
}



class Teacher
{
public:
    int age;
    char name[32];

    void printT()
    {
        cout << "age :" << age <<endl;
    }
}

void main()
{
    Teacher t1,t2,t3;
    t1.age = 31;
    t2.age = 32;
    t3.age = 33;

    queue<Teacher > q;
    q.push(t1);
    q.push(t2);
    q.push(t3);

    while (!q.empty())�{

        Teacher tmp = q.front();
        tmp.printT();
        q.pop();
    }

}

    4)list和forward_list在其他地方插入删除异常的快,随机探访非常的慢。

STL中的list容器

list容器械备如下特点:

  • 能够在头顶和后面部分插入和删除成分
  • 不能够随便访谈成分,也正是迭代器只好只可以++,无法三遍性跳转

  [4]关系容器的利用验证:

list的基本操作

int main(int argc, const char * argv[]) {

    //创建list对象
    list<int> l;

    //尾部添加元素
    for (int i = 0; i < 10; i++) {
        l.push_back(i);
    }

    //头部添加元素
    l.push_front(111);

    //遍历
    for (list<int>::iterator it = l.begin(); it != l.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;

    //list不能随机访问
    list<int>::iterator it = l.begin();
    it++;
    it++;
    cout << *it <<endl;
//    it = it + 1;  //编译报错,不能随机访问
//    it = it + 5;  //编译报错,不能随机访问


    return 0;
}

    1)map的行使,map是叁个键值对,当中值是一个pair对象:

list的删除

list提供了多个函数用来删除成分,分别是eraseremove

  • erase是通过岗位依旧区间来删除,主要构成迭代器指针来操作
  • remove是由此值来删除

int main(int argc, const char * argv[]) {

    //创建list对象
    list<int> l;

    //添加数据
    for (int i = 0; i < 10; i++) {
        l.push_back(i);
    }
    l.push_back(100);
    l.push_back(100);

    //删除头部元素
    l.erase(l.begin());

    //删除某个区间
    list<int>::iterator it = l.begin();
    it++;
    it++;
    it++;
    l.erase(l.begin(), it);

    //移除值为100的所有元素
    l.remove(100);

    //遍历
    for (list<int>::iterator it = l.begin(); it != l.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;

    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <arpa/inet.h>

using namespace std;

class ca
{
public:
    int ip;
};

class comp{
public:
    bool operator()(const ca &a, const ca &b) 
    {
        return a.ip < b.ip;
    }
};

bool operator<(const ca &a, const ca &b)
{
    return a.ip < b.ip;
}

int main()
{
    //map的第三个参数,另外第三个参数使用普通函数时编译不过不知怎么回事
    ca a1, a2, a3;
    map<ca, string> addr2;            //使用全局的<运算符重载函数
    map<ca, string, comp> addr1;    //使用仿函数

    addr1.insert(pair<ca, string>(a1, "1 addr a1"));
    addr2.insert(pair<ca, string>(a1, "2 addr a1"));


    //pair的使用
    pair<ca, string> p1(a1, "1 addr a1");              //创建一个pair对象
    addr1.insert(p1);
    addr1.insert(pair<ca, string>(a2, "1 addr a2"));   //隐式的创建一个pair对象,这个是常用的方式,因为通常没有一个现成的对象
    addr1.insert(make_pair(a2, "1 addr a3"));          //调用make_pair创建一个pair对象


    //关联容器定义的类型
    /*
        map的元素类型是一个pair对象,这个很重要。
        元素:      map的元素就是map容器里面的一个值。就像是set<int>的每个元素是一个int型对象。
        关键字: map的关键字是pair对象的第一个元素。
    */
    map<ca, string>::key_type     v1;     //v1的类型是类ca,代表关键字的类型
    map<ca, string>::mapped_type  v2;     //v2的类型是string,代表关键字关联的类型
    map<ca, string>::value_type   v3;     //v3的类型是pair<ca, string>,代表值的类型

    set<string>::key_type    v4;          //v4和v5的类型是string,set的键值类型是一样的
    set<string>::value_type  v5;        
    return 0;
}

STL中的priority_queue优先级队列容器

刚开始阶段级队列分为:最小值优先队列和最大值优先队列。

这里的最大值、最小值是指队头的要素(增序、降序)。默许,是开创最大值优先级队列。

概念优先级的格局:

  • priority_queue<int>默断定义int类型的最大值队列
  • priority_queue<int, vector<int>, less<int>>定义int型的最大值优先队列
  • priority_queue<int, vector<int>, greater<int>>定义int型的蝇头值队列

地方的概念中,lessgreater一定于谓词,是预定义好的排序函数,大家称为“仿函数”。后边会介绍它的用法。

void main()
{
    //定义优先级队列(默认是最大值优先级队列)
    priority_queue<int> p1;

    //定义一个最大优先级队列
    //less是提前定义好的预定义函数 相当于谓词
    priority_queue<int, vector<int>, less<int>> p2;

    //定义一个最小值优先级队列v
    priority_queue<int, vector<int>, greater<int>> p3;

    //给默认的最大优先级队列入栈
    p1.push(33);
    p1.push(11);
    p1.push(55);
    p1.push(22);

    //打印最大优先级的对头元素
    cout<<"对头元素:"<< p1.top() <<endl;
    cout<<"队列的大小:"<< p1.size() <<endl;

    //给最小值优先级队列入栈
    p3.push(33);
    p3.push(11);
    p3.push(55);
    p3.push(22);

    //打印最小优先级队列的对头元素
    cout<<"对头元素:"<< p3.top() <<endl;
    cout<<"队列的大小:"<< p3.size() <<endl;

}

    2)关键字类型必需定义成分的相比较艺术,那一个是必定的,不然不能够进展排序。实际编制程序中,如若二个类定义了<运算符,就足以看成第一字了,这几个意思乃是首要字不须要落成>和=运算符,实现了和不达成效果与利益是一模一样的。还也可能有正是定义<运算符要符合严俊弱序的条条框框,那意思就是自定义的<运算符函数,实现要合理,比方传给那函数二个(a,b),再次来到true,传给三个(b,a),照旧回到true,那就不适合严苛弱序了。即使不切合不过不会形成编写翻译错误,只是用的时候会发出各个错误,比如set插入了同样的成分。

STL中的set和multiset集合容器

set是三个凑合,Objective-C言语中也可以有汇集的定义。C++中的集合与OC中的集结也许有例外的地点。

  • C++的set容器,其中包括的成分是唯一的,何况是稳步的。
  • C++的set容器,是依据顺序插入的,不能在内定地点插入。
  • C++的set容器,其组织是红黑二叉树,插入数据的频率比vector快

    
 严谨弱序法规正是:(1)假诺传入(a,b)再次来到true,则传出(b,a)必需回到false。

set成分的插入和删除

set提供了inserterase爱博体育,函数,用来对成分进行扦插和删除操作。

  • 基本功项目数据,若是插入的是再一次的因素,则插入失利,重返值是二个pair类型
  • pair类型类似于swift语言中的元组的概念,通过其判定是不是插入成功
  • 复杂类型数据,须要通过仿函数来规定因素的相继,步入判别是不是是重复成分。在“自定义对象的排序”里面疏解。

void main()
{
    set<int> set1;

    //插入元素
    for (int i = 0; i<5; i++) {
        int tmp = rand();
        set1.insert(tmp);
    }

    //重复插入元素(会插入不成功,下一节会分析如果根据返回值判断是否插入成功)
    set1.insert(100);
    set1.insert(100);
    set1.insert(100);
    set1.insert(100);

    for (set<int>::iterator it = set1.begin(); it != set1.end(); it++) {
        cout << *it <<" ";
    }


    //删除集合
    while(!set1.empty())
    {
        //获取头部
        set<int>::iterator it = set1.begin();

        //打印头部元素
        cout << *it << endl;

        //从头部删除元素
        set1.erase(set1.begin());
    }

}

                
(2)假若传入(a,b)再次来到true,传入(c,a)重返true,则传出(c,b)必需回到true。就是说a<b,c<a,则c要低于b。

经常数据类型的排序

set容器是不改变的汇聚,暗中同意的依次是从小到大的。

创建集结的措施:

  • set<int>创设默许的从小到大的int类型的成团
  • set<int,less<int>>始建四个从小打到大的int类型的聚合
  • set<int,greater<int>>创制一个从大到小的int类型的集聚

地方的less和greater便是仿函数,集结会依据这些仿函数的重临值是或不是为真类实行排序。

//仿函数的原型,下面是C++提供的默认的greater的仿函数(删除了宏定义后的)
struct greater
{
    bool operator()(const int &left, const int &right) const
    {
        //如果左值>右值,为真。从大到小的排列
        return left > right;
    }
};

我们得以测量检验下,加多进set群集的要素确实是有顺的。

void main()
{
    //默认,从小到大
    set<int> set1;
    //从小到大--默认就是
    set<int, less<int>> set2;
    //从大到小
    set<int, greater<int>> set3;

    //添加元素
    for (int i = 0; i < 5; i++) {
        int tmp = rand();
        set3.insert(tmp);
    }

    //遍历
    for (set<int>::iterator it = set3.begin(); it != set3.end(); it++) {
        cout<< *it << " ";
    }
}
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <set>
#include <arpa/inet.h>

using namespace std;

class ca
{
public:
    unsigned ip;
    int port;
    ca(int ip1, int port1) {ip = ip1; port = port1;}

    //怎么定义<运算符都是可以的,看需求。
    //只是定义不同时,元素的排序顺序是不一样的。

    //如果用这种方式,则set里面的排列顺序为:
    //    1.1.1.1:80
    //    1.1.1.1:81
    //    1.1.1.2:80
    //    1.1.1.2:81
    //        
    //这意思就是用ip去比较地址,如果ip不同时再用port去比较地址,当然使用这种需求一般用的比较多,这种方让元素之间有一层优先级。
    bool operator<(const ca &other) const{
        if(ip < other.ip) 
            return true;
        else if(other.ip < ip) 
            return false;

        if(port < other.port) 
            return true;
        else if(other.port < port) 
            return false;

        return false;
    }

    //完全等同于上面的函数
    bool operator<(const ca &other) const{
        if(ip < other.ip ||(ip == other.ip && port <other.port)) 
            return true;

        return false;
    }

    //如果用这种方式,则set里面的排列顺序为:
    //    1.1.1.1:80
    //    1.1.1.2:80
    //    1.1.1.1:81
    //    1.1.1.2:81
    //这意思就是说希望用ip或port去比较地址,只要有一个小于就是小于,很明显这种方式用的少。
    bool operator<(const ca &other) const
    {
        if(ip < other.ip || port < other.port) 
            return true;

        return false;
    }

    //错误的方式,错误的函数不会导致编译错误,但是处理的时候会发生错误。例如下面这种就可以把相同的数据插入到set里面。
    //很明显已经违背了严格弱序的原则,因为a < b为true的时候同时b < a也为true。
    bool operator<(const ca &other) const{
        return true;
    }
};

int main()
{
    set<ca> v;
    set<ca>::iterator it;

    v.insert(ca(inet_addr("1.1.1.1"), 80));
    v.insert(ca(inet_addr("1.1.1.1"), 81));
    v.insert(ca(inet_addr("1.1.1.2"), 80));
    v.insert(ca(inet_addr("1.1.1.2"), 81));

    for(it=v.begin(); it!=v.end(); it++)
        printf("%s:%d\n", inet_ntoa(*((struct in_addr *)&(it->ip))), it->port);

    return 0;
}

自定义对象的排序

地点,大家看到了基础的数据类型的集合,是可以排序的。不过,假使会集里面放的是异样的自定义对象,该怎么知足set有序的风味呢?

透过地方的例证,我们领略,基础数据类型的set是不改变的主要原因是greater和less仿函数。所以,自定义对象的静止也是经过大家自定义仿函数来确认保障的。

仿函数,之所以叫仿函数,是因为它跟函数很像,但实际不是二个函数。它的结果如下,只要大家兑现了这几个仿函数,大家也得以对自定义对象举行排序。

//定义仿函数的结构体
struct FunctionName
{
    //重载了()运算符,实现两个自定义对象的比较
    bool opeartor() (Type &left, Type &right)
    {
        //左值大于右值,从大到小的顺序
        if(left > right) 
            return true;
        else
            return false;

    }
};

上边,大家自定义一个Student对象,依据年龄进行排序,将目的参预到set集结中,并实行打字与印刷。

假定仿函数完结了基于年龄进行排序,因为set是因素唯一的,所以在插入对象的时候,假设年龄是再一次的,则插入不进去了。

//定义student对象
class Student {
public:
    Student(const char *name, int age)
    {
        strcpy(this->name, name);
        this->age = age;
    }

public:
    char name[64];
    int  age;
};

//提供仿函数,用于自定义对象的set进行排序,要写一个仿函数,用来排序
struct FuncStudent
{
    //重载了括号操作符,用来比较大小
    bool operator() (const Student &left, const Student &right)
    {
        //如果左边比右边小,从小到大按照年龄排序
        if(left.age < right.age)
            return true;
        else
            return false;
    }

};

void main()
{
    Student s1("s1",32);
    Student s2("s2",22);
    Student s3("s3",44);
    Student s4("s4",11);
    Student s5("s5",22); 

    //创建集合,采用从小到大的排序
    set<Student, FuncStudent> set1;

    //插入数据
    set1.insert(s1);
    set1.insert(s2);
    set1.insert(s3);
    set1.insert(s4);
    //插入不进去(年龄有重复的,所以插不进去了),要通过返回值来确保是否插入成功
    set1.insert(s5);    

    //遍历
    for (set<Student>::iterator it = set1.begin(); it != set1.end(); it++) {
        cout << it->age << "\t" << it->name <<endl;
    }

}

    3)set的迭代器是const的,正是说不能够通过一个set迭代器去修改多少个值,这些也是自然的,因为set是平稳的,修改了就严节了,同理map的key值也是不能够因而迭代器修改的,然而map的value是能够通过迭代器修改的。

pair类型的重返值

pair类型,就类似于斯威夫特语言中的“元组”的概念,那些系列蕴涵了四个数据类型,在函数再次来到的时候,能够何况重临五个值。

大家来看一下pair类型的定义它实际上是一个结构体。它蕴含了五个属性,firstsecond

template <class _T1, class _T2>
struct pair
{
    typedef _T1 first_type;
    typedef _T2 second_type;

    _T1 first;
    _T2 second;
}

上边的例子中,大家驾驭set集结中的成分是独一的,重复的成分插入会倒闭。假设判定是不是插入成功,大家能够通过insert函数的再次来到值来判断,它的再次回到值是贰个pair项目。大家来看一下insert函数的原型:

pair<iterator,bool> insert(const value_type& __v)

回到的是pair<iterator, bool>项目,pair的首先个天性表示近来安排的迭代器的职位,第一个属性表示插入是还是不是中标的bool值。所以,大家能够透过第二个天性来决断成分是不是插入成功。

//pair的使用判断set的insert函数的返回值
void test3()
{
    Student s1("s1",32);
    Student s2("s2",22);
    Student s3("s3",44);
    Student s4("s4",11);
    Student s5("s5",22);

    //创建集合,采用从小到大的排序
    set<Student, FuncStudent> set1;

    //插入数据,接收返回值
    pair<set<Student, FuncStudent>::iterator, bool> pair1 = set1.insert(s1);
    if (pair1.second == true) {
        cout << "插入s1成功" <<endl;
    }else{
        cout << "插入s1失败" <<endl;
    }
}

    4)map和set的insert操作能够检查重回值,判定是或不是插入成功,举个例子成分已经存在则insert会退步,重回的pair类型的第2个值为false。

set容器数据的探寻

set容器提供了多少个函数用来搜寻成分

  • iterator find(const key_type& __k) find函数查找成分为k的迭代器地方
  • iterator lower_bound(const key_type& __k)
    lower_bound函数查找小于等于成分k的迭代器地点
  • iterator upper_bound(const key_type& __k)
    upper_bound函数查找大于成分k的迭代器地点
  • pair<iterator,iterator> equal_range(const key_type& __k)
    equal_range函数重回叁个pair类型,第贰天本性表示大于等于k的迭代器地方,第三个是大于k的迭代器地点

void test4()
{
    set<int> set1;

    for (int i = 0; i < 10; i++) {
        set1.insert(i+1);
    }

    //遍历
    for (set<int>::iterator it = set1.begin(); it != set1.end(); it++) {
        cout << *it <<endl;
    }

    //查找元素是5的迭代器位置
    set<int>::iterator it0 = set1.find(5);
    cout << "it0:" << *it0 <<endl;

    //查找小于等于5的元素迭代器位置
    set<int>::iterator it1 = set1.lower_bound(5);
    cout << "it1:" << *it1 <<endl;

    //查找大于5的元素迭代器位置
    set<int>::iterator it2 = set1.upper_bound(5);
    cout << "it2:" << *it2 <<endl;

    //返回的pair第一个迭代器是>=5,另一个是>5
    pair<set<int>::iterator, set<int>::iterator> mypair = set1.equal_range(5);
    set<int>::iterator it3 = mypair.first;
    set<int>::iterator it4 = mypair.second;
    cout << "it3:" << *it3 <<endl;
    cout << "it4:" << *it4 <<endl; 
}

    5)lower_bound和upper_bound:那三个操作都回到三个迭代器,要是首要字在容器中,lower_bound再次来到的迭代器指向第七个因素,upper_bound再次来到的迭代器指向最后三个职责的将来;假如重要字不在容器中,则lower_bound和upper_bound重回一样的职责,那个任务就是不影响排序的要害字的插入地方。借使存在则只要(begin=lower_bound;begin!=upper_bound;begin++;)就足以遍历这么些第一字对应的保有因素。借使一纸空文,则赶回的迭代器指向的职责是要查的最首要字可以插入的职务。

multiset容器

multiset容器,与set容器相似,可是multiset容器中的成分得以再次。别的,他也是活动排序的,容器内部的值不可小视修改,因为有各类的。

void test5()
{
    //定义multiset
    multiset<int> set1;

    //从键盘不停的接收值
    int tmp = 0;
    printf("请输入multiset集合的值:");
    scanf("%d", &tmp);
    while (tmp != 0) {
        set1.insert(tmp);
        scanf("%d", &tmp);
    }

    //迭代器遍历
    for (multiset<int>::iterator it = set1.begin(); it != set1.end(); it++) {
        cout<< *it <<" ";
    }
    cout <<endl;

    //删除
    while (!set1.empty()) {
        multiset<int>::iterator it = set1.begin();
        cout << *it << " ";
        set1.erase(it);
    }
}

    6)multimap的应用(想必multiset的用法也是基本上的)例:

STL中的map和multimap映射容器

map和multimap是一个键值映射的容器。map中的键值对都以独一的,但是multimap中四个键能够对应多少个值。

  • map和multimap是关联式容器,键值成对存在
  • map和multimap是红黑变体的平衡二叉树结构
  • map只帮忙独一的键值对,集结中的成分是安份守己一定的顺序排列的
  • multimap中的键能够出现多次
  • map和multimap的要素插入进度是按部就班顺序插入的
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <arpa/inet.h>

using namespace std;

/*
    查找mulimap关键字的方法
*/
int main()
{
    // 1.使用lower_bound和upper_bound
    multimap<int, string> m;
    multimap<int, string>::iterator begin, end;

    m.insert(pair<int, string>(1, "boy1"));
    m.insert(pair<int, string>(2, "girl1"));
    m.insert(pair<int, string>(2, "girl2"));
    m.insert(pair<int, string>(2, "girl3"));
    m.insert(pair<int, string>(5, "dog1"));
    m.insert(pair<int, string>(5, "dog2"));

    /*
        输出:
            key 2 value:girl1
            key 2 value:girl2
            key 2 value:girl3
            end value:dog1
        可见begin指向(2, "girl1"),end指向(5, "dog1")。
    */
    begin = m.lower_bound(2);
    end   = m.upper_bound(2);
    while(begin != end){
        printf("key 2 value:%s\n", begin->second.c_str());
        begin++;
    }
    printf("end value:%s\n", end->second.c_str());

    /*
        输出:
            begin value:dog1
            end   value:dog1
        可见begin 和end都指向(5, "dog1"),因为此位置就是要查的关键字3应该插入的位置。
    */
    begin = m.lower_bound(3);
    end   = m.upper_bound(3);
    printf("begin value:%s\n", begin->second.c_str());
    printf("end   value:%s\n", end->second.c_str());

    /*
        输出:
            begin value:0x7fff4e76b578
            end   value :0x7fff4e76b578
            m.end value:0x7fff4e76b578
        可见begin 和end都指向m.end(),因为此位置就是要查的关键字10应该插入的位置。
    */
    begin = m.lower_bound(10);
    end   = m.upper_bound(10);
    printf("begin value:%p\n", begin);
    printf("end   value:%p\n", end);
    printf("m.end value:%p\n", m.end());


    // 2.使用count方法得到个数
    int count;
    multimap<int, string>::iterator it;

    count = m.count(5);    //得到关键字5的元素个数
    it = m.find(5);     //指向关键字5的第一个元素

    /*
        输出:
            key 5 value:dog1
            key 5 value:dog2
    */
    while(count > 0){
        printf("key 5 value:%s\n", it->second.c_str());
        it++;
        count--;
    }

    // 3.使用equal_range
    pair<multimap<int, string>::iterator, multimap<int, string>::iterator> range;

    range = m.equal_range(2); //直接得到关键字2的迭代器的起始位置

    /*
        输出:
            key 2 value:girl1
            key 2 value:girl2
            key 2 value:girl3
    */
    while(range.first != range.second){
        printf("key 2 value:%s\n", range.first->second.c_str());
        range.first++;
    }

    return 0;
}

map成分的增加和删除改查

map成分的插入,有二种方式:

  1. 调用insert函数插入pair类型的键值对
  2. 一向利用[]来对键实行理并答复制,类似于Objective-C中的NSMutableDictionary赋值同样。

map的insert函数重返的是pair类型,pair的第贰个参数表示是还是不是插入成功。假设插入的成分键名一样,则插入退步。

map元素的删除,跟上边其余的器皿同样,都以一向调用erase函数.

int main()
{
    map<int, string> map1;

    //insert方法插入
    //--1 通过pair<int, string>(1,”chenhua“) 构造pair元素
    map1.insert(pair<int, string>(1,"chenhua"));
    //--2 通过make_pair构造pair元素
    map1.insert(make_pair(2,"mengna"));
    //--3 通过value_type构造pair元素
    map1.insert(map<int, string>::value_type(3,"chenmeng"));

    //[]直接插入
    map1[4] = "menghua";

    //重复插入(插入会不成功)
    pair<map<int, string>::iterator, bool> pair1 = map1.insert(make_pair(2, "haha"));
    if (pair1.second) {
        cout << "重复插入成功" << endl;
    }else{
        cout << "重复插入失败" << endl;
    }

    //元素的修改
    //map[1] = "22"的方式,如果不存在键则插入,存在键则修改
    map1[2] = "haha";

    //元素的删除
    //--删除值为"haha"的元素
    for (map<int, string>::iterator it = map1.begin(); it != map1.end(); it++) {
        if (it->second.compare("haha") == 0) {
            map1.erase(it);
        }
    }

    //遍历
    for (map<int, string>::iterator it = map1.begin(); it != map1.end(); it++) {
        cout << it->first << "\t" << it->second << endl;
    }

    return 0;
}

    7)unordered_map和unordered_set:这多少个容器是冬季的,便是说不是按顺序存款和储蓄的。因为map和set都以自动排序的,举例遍历map或set,能够获得按梯次排序的值。但是unordered_map和unordered_set是按hash存款和储蓄的,遍历会获取不按顺序输出的值,所以一旦供给存款和储蓄的要素有序就用map,要是严节而且须要神速搜索,就用unordered_map。map和unordered_map效用相比的例证:

map成分的查找

map提供了八个函数实行key的搜索:find和equal_range。

int main()
{
    //定义map
    map<int ,string> map1;
    map1[1] = "chenhua";
    map1[2] = "mengna";

    //查找key=100的键值对
    map<int, string>::iterator it = map1.find(100);
    if (it != map1.end()) {
        cout << "存在key=100的键值对";
    }else{
        cout << "不存在" << endl;
    }


    //查找key = 1000的位置
    //返回两个迭代器,第一个表示<=1000的迭代器位置,第二个是>1000的迭代器位置
    pair<map<int, string>::iterator, map<int, string>::iterator> mypair = map1.equal_range(1000);
    if (mypair.first == map1.end()) {
        cout << "大于等于5的位置不存在" << endl;
    }else{
        cout << mypair.first->first << "\t" << mypair.first->second << endl;
    }

    if (mypair.second == map1.end()) {
        cout << "大于5的位置不存在" << endl;
    }else{
        cout << mypair.second->first << "\t" << mypair.second->second << endl;
    }

    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <arpa/inet.h>
#include <tr1/unordered_map>
using namespace std::tr1;

using namespace std;

double timeuse(struct timeval begin, struct timeval end)
{
    double res;

    if(end.tv_usec < begin.tv_usec){
        end.tv_usec += 1000000;
        end.tv_sec--;
    }

    res = end.tv_usec - begin.tv_usec;
    res += (end.tv_sec - begin.tv_sec)*1000000;
    return res;
}

int main()
{
    int i;

    map<int, int> m;
    map<int, int>::iterator it;
    unordered_map<int, int> um;
    unordered_map<int, int>::iterator uit;
    struct timeval tv_begin, tv_end;
    double res;

#define BEGIN   gettimeofday(&tv_begin, NULL);
#define END(s)  do{\
                    gettimeofday(&tv_end, NULL); \
                    res = timeuse(tv_begin, tv_end); \
                    printf("%-30s, usetime:%f usec [%f sec]\n", s, res, res/1000000);\
                }while(0)

    BEGIN
    for(i=0; i<10000000; i++)
        m.insert(pair<int, int>(i, i));
    END("map insert");

    BEGIN
    for(i=0; i<10000000; i++)
        um.insert(pair<int, int>(i, i));
    END("unordered_map insert");

    BEGIN
    it = m.find(500000);
    END("map find");

    BEGIN
    uit = um.find(500000);
    END("unordered_map find");

    return 0;
}

multimap容器

multimap容器,与map容器的独一分歧是:multimap援助多个键值。

鉴于协理四个键值,multimap提供了cout函数来计量同四个key的要素个数。

class Person {


public:

    string name;    //姓名
    int age;        //年龄
    string tel;     //电话
    double sal;     //工资

};

void test()
{
    Person p1,p2,p3,p4,p5;
    p1.name = "王1";
    p1.age  = 31;

    p2.name = "王2";
    p2.age  = 31;

    p3.name = "张3";
    p3.age  = 31;

    p4.name = "张4";
    p4.age  = 31;

    p5.name = "钱5";
    p5.age  = 31;


    multimap<string, Person> map2;

    //sale部门
    map2.insert(make_pair("sale", p1));
    map2.insert(make_pair("sale", p2));

    //development部门
    map2.insert(make_pair("development", p3));
    map2.insert(make_pair("development", p4));

    //Finanncial部门
    map2.insert(make_pair("Finanncial", p5));


    //遍历
    for (multimap<string, Person>::iterator it = map2.begin(); it != map2.end(); it++) {


        cout << it->first << "\t" << it->second.name << endl;

    }

    //按部门显示员工信息
    int developNum = (int) map2.count("development");
    cout << "development部门人数:" << developNum << endl;
    multimap<string,Person>::iterator it2 = map2.find("development");
    int tag = 0;
    while (it2 != map2.end() && tag < developNum) {
        cout << it2->first << "\t" << it2->second.name <<endl;
        it2 ++;
        tag ++;
    }

    //把age=32 修改name= 32
    for (multimap<string, Person>::iterator it = map2.begin(); it != map2.end(); it++) {
        if (it->second.age == 32) {
            it->second.name = "32";
        }
    }
}

int main(int argc, const char * argv[]) {

    test();

    return 0;
}

  输出:

STL容器的通用性探讨

到此处,STL的容器我们着力教学结束了。STL的器皿重要行使了C++的模版天性来兑现。需求留神:

  • 容器缓存了节点,节点类要确认保证援救拷贝(不然出现浅拷贝难题,导致崩溃)
  • 容器中的一般节点类,需求提供拷贝构造函数,同样珍视载等号操作符(用来赋值)
  • 容器在插入成分时,会活动举行成分的正片。

本着容器,容器之间也支撑拷贝。所以需求注意:

  • 除去queue和stack外,各样容器都提供了可重返迭代器的函数,运用重回的跌打器就足以访问元素
  • 常常STL不会抛出非常,供给使用者确认保障传入正确的参数
  • 每一个容器都提供了三个暗中同意构造函数和二个暗许拷贝构造函数
map insert                    , usetime:10102899.000000 usec [10.102899 sec]
unordered_map insert          , usetime:2749505.000000 usec [2.749505 sec]
map find                      , usetime:3.000000 usec [0.000003 sec]
unordered_map find            , usetime:1.000000 usec [0.000001 sec]

STL容器的要素拷贝

上面,大家演示一下,假若容器元素若无兑现拷贝构造函数,出现浅拷贝后的崩溃难点。

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


class Student {

public:
    Student(const char *name, int age)
    {
        cout << "构造函数" << endl;

        //分配内存空间
        m_name = new char[strlen(name) + 1];
        //值拷贝
        strcpy(m_name, name);

        m_age = age;
    }


    ~Student()
    {
        printf("%p 指向的空间 调用析构函数\n", m_name);
        if (m_name != NULL) {
            delete []m_name;
            m_age = 0;
        }
    }

private:
    char *m_name;
    int   m_age;

};


int main()
{
    Student s1("chenhua",24);

    vector<Student> v1;
    v1.push_back(s1);

    return 0;
}

地方的代码段,运转后的结果如下:

构造函数
0x100302a00 指向的空间 调用析构函数
0x100302a00 指向的空间 调用析构函数

运转后,打字与印刷出结果后并报错。报错原因是同二个内部存款和储蓄器空间被放飞了2次,导致的垮台。其根本原因是,v1将s1拷贝到容器,由于Student未有重写拷贝构造函数,进而出现了浅拷贝,只拷贝了地址。释放的时候断定出现错误。

万一大家给Student重写了拷贝构造函数和重载了等号操作符,则下面的一无所能就不会油然则生。

//重写拷贝构造函数
Student(const Student &obj)
{
    //分配内存空间
    m_name = new char[strlen(obj.m_name) + 1];
    //值拷贝
    strcpy(m_name, obj.m_name);

    m_age = obj.m_age;
}

//重载等号运算符
Student & operator=(const Student &obj)
{
    //释放旧值
    if (m_name != NULL) {
        delete [] m_name;
        m_age = 0;
    }

    //分配内存空间并赋值
    m_name = new char[strlen(obj.m_name) + 1];
    strcpy(m_name, obj.m_name);
    m_age = obj.m_age;

    return *this;
}

  同理可得unordered_map在插入和探寻方面依然比map要快比比较多的,当然内存占用的也多。

STL容器的比较

STL提供了大多容器,各样容器有其自身的风味,我们该怎么选拔它们啊?

vector deque list set mutliset map multimap
内存结构 单端数组 双端数组 双向链表 二叉树 二叉树 二叉树 二叉树
随机存取 对key而言是
查找速度 非常慢 对key而言快 对key而言快
插入删除 尾端 头尾两端 任何位置 $1

3.STL适配器

  [1]stack:栈适配器。这几个要注意的就是栈里面是有三个器皿来积攒成分的,常规的知道栈应该正是叁个数组,其实不是,栈暗中同意的器皿是多少个双向队列(deque),栈也足以用vector、list等作为成分的器皿,有个典型正是便于须求提供push_back()、back()、和pop_back()方法,因为set未有提供那几个方式,所以无法把set作为栈的容器。所谓栈正是在多少个器皿上边加了有的操作函数,封装成了栈模板(STL中栈定义:template
< class T, class Container = deque<T> > class stack;)。

  [2]queue:单向队列适配器。那个和stack是一模二样的,特点是先进先出。当然也是内需提供叁个器皿来囤积成分,暗许是deque。

  [3]priority_queue
:优先级队列适配器。那一个适配器是把容器内的要素排序好的,每回能够赢得优先级最高的成分。所以能够流传二个先行级比较函数,暗中同意比较函数是<,暗中同意的容器是vector,定义情势priority_queue<Type,
Container,
Functional>。其实这一个适配器只是在queue上边加了三个优先级排序函数。

4.STL切实用法

  [1]const_iterator:若是没有要求写访问容器时,应该利用const_iterator迭代器,方法和iterator是大同小异的。

  [2]reverse_iterator(const_reverse_iterator):能够从后迈入遍历容器。

  [3]管理体积:除了array之外,容器的内部存款和储蓄器都是活动管理的。size()表示目前的因素数量,capacity()表示曾经预分配的体量,reserve()表示设置预分配的体积。那一个意思乃是capacity()是早就预分配好的体积,不管是前后相继自动分配依旧手动调用reserve()分配。还应该有正是resize()是设置容器使用的轻重,和预分配未有涉嫌,比方当前因素个数是5,则resize(8)会往容器扩大3个要素,resize(4)会删除1个元素。

  [4]substr:拷贝原始string的一部分或任何。其余string类还提供了有个别别的办法,来变成和char
*花色的字符串的管理函数,举个例子附近strstr、strrstr的函数。

  举例:

#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <array>

using namespace std;

int main()
{
    vector<int> v;
    vector<int>::const_iterator c_it;
    vector<int>::const_reverse_iterator cr_it;

    v.push_back(1); v.push_back(2); v.push_back(3);v.push_back(4);v.push_back(5);

    //iterator对应的是begin、end
    for(c_it = v.begin(); c_it!= v.end(); c_it++)
        printf("%d\n", *c_it);

    //reverse_iterator对应的是rbegin、rend,cr_it = v.begin()是编译不过的
    for(cr_it = v.rbegin(); cr_it!= v.rend(); cr_it++)
        printf("%d\n", *cr_it);

    // 容量设置
    printf("size:    %u\n", v.size());
    printf("capacity:%u\n", v.capacity());        //程序自动预分配的容量,例如值是8
    printf("--------------------------\n");

    v.reserve(20);                                //手动设置预分配的容量,如果值小于已经自动预分配的容量则不生效
    printf("size:    %u\n", v.size());
    printf("capacity:%u\n", v.capacity());        //此时值就是20了
    printf("--------------------------\n");

    v.resize(200);                                //强制设置元素个数是200                                 
    printf("size:    %u\n", v.size());            //值是200
    printf("capacity:%u\n", v.capacity());        //超过预分配的容量后,程序就会再次自动预分配

    //string操作
    string src("test string");
    string dst;

    // substr(pos, n)   pos默认是0,n默认是总长度,所以只传一个参数的时候其实是指定了拷贝的起始位置
    dst = src.substr(3);      
    printf("dst:%s\n", dst.c_str());             // dst:"t string" 
    dst = src.substr();      
    printf("dst:%s\n", dst.c_str());             // dst:"test string"

    return 0;
}

 5.C++手册

  http://www.cplusplus.com/reference/

 

  

相关文章