React连忙入门教程管理

STL(标准模板库),是近期C++内置支持的library。它的底层利用了C++类模板和函数模板的编写制定,由三大学一年级些组成:容器、算法和迭代器。

简介

  Twitter官网介绍:React 是多少个用来创设用户界面包车型大巴 JavaScript
库。相当于 MVC 架构的 V 层。

  React
的焦点理想是:封装组件,种种零部件维护和谐的状态和UI,当状态变更,自动重新渲染整个组件。

  React的亮点(React为啥如此火?):

    管理 1

  首先在攻读React从前,你须要对以下文化具有了解:

  1. 原生JavaScript基础

  2. CSS基础

  3. npm包管理基础

  4. webpack营造项目基础

  5. ES6规范

当下STL有六大组件

简言之的采纳  

  React营造界面包车型大巴叁要点:组件、路由、状态管理。

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

 1. 组件

  React的组件撰写和调用重要借助于ES陆的模块化和JSX的语法。

【 main.js 】
-------------
import React from 'react-dom';
import ReactDOM from 'react-dom';
import MyComponent from './subComponent/component.js';
// 主组件
class mainComponent extends React.Component {
    render() {
        return (
          <div className="box">
              //包含子组件
              <MyComponent/>
          </div>
        );
    }
}

// 显示组件,使用下面这个方法 render(要显示的组件,组件显示的位置)
ReactDOM.render(
    <MyComponent/>,
    document.getElementById('app')
);
【 component.js 】
------------------
import React from 'react';
// 子组件 (定义组件,即定义一个类)
class MyComponent extends React.Component {
    // 返回组件要显示的内容
    render() {
        return (
            // 使用HTML格式
            <div>
                <h1>这是一个组件!</h1>
            </div>
        );
    }
}
export default MyComponent; //将定义的组件作为模块默认的组件导出
【 main.css 】
-------------
.box {
    width: 100%
}

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

  React首要的优点正是增量更新(虚拟DOM)和组件化(状态机)。

    虚拟DOM:进步页面包车型大巴质量,幸免XSS攻击。

  React 的大旨内容:JSX和编造 DOM。

    3个最中央的 React
组件由数量和JSX几个首要部分组成,大家先来看望数据。

  • 那是1个大约完整的React组件(类),props
    首要功用是提供数据来源,能够简不难单的敞亮为 props 便是构造函数的参数。
    state
    唯壹的法力是决定组件的展现,用来存放在会随着交互变化境况,比如开关状态等。JSX
    做的事体正是依照 state 和 props
    中的值,结合一些视图层面的逻辑,输出对应的 DOM 结构。

     在组件内部,能够由此this.props来访问props

     React的一大创新,正是把每三个零件都用作是三个状态机,组件内部通过state来保卫安全组件状态的变更,这也是state唯1的法力。

  •  与传统App比较

    管理 2

  • 古板 Web
    App:是一贯和DOM交互,由App来决定DOM的营造和渲染、成分属性的读写、事件的挂号和销毁等。
  • React Web
    App:是由此编造DOM来交互。虚拟DOM是在DOM的基本功上建立了1个抽象层,大家对数据和景观所做的任何变动,都会被机关且不慢的一块到虚拟DOM,最终再批量联合到DOM中。(渲染功效高)

   React最近协助的事件列表:

    管理 3

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

生命周期

  大体能够分成八个经过:先导化、更新和销毁,在组件生命周期中,随着组件的props或许state发生转移,它的杜撰DOM和DOM表现也将有相应的转变。

STL初探

容器是STL中很关键的壹种数据结构。常见的容器包罗

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

除此而外容器,STL还包裹了强有力的算法,能够落实查找、删除、替换、删除等居多常见操作。后边会首要教学。

除此以外,迭代器也是STL主要的一环,通过迭代器,我们得以很便宜对容器中的成分进行遍历,以及操作容器。后边我们会穿插讲解迭代器。

  一、初始化:

  管理 4

  解析:

  1. 组件类在宣称时,会先调用 getDefaultProps()
    方法来博取暗许props值,这些措施会且只会在宣称组件类时调用1回,那或多或少索要专注,它回到的暗许props由具有实例共享。
  2. 在组件被实例化此前,会先调用一回实例方法 getInitialState()
    方法,用于获取这些组件的伊始state。
  3. 实例化之后正是渲染,component威尔Mount方法会在生成虚拟DOM从前被调用,你能够在此处对组件的渲染做一些备选工作,比如总括目的容器尺寸然后修改组件自己的尺码以适应目的容器等等。
  4. 接下去正是渲染工作,在那边您会创立贰个虚拟DOM用来表示组件的结构。

  对于3个组件来说,render
是绝无仅有三个必须的措施。render方法须求满意这几点:

      一)只好通过 this.props 或 this.state 访问数据

      贰)只好出现一个拔尖组件

      三)能够重回 null、false 或任何 React 组件

      肆)不能够对 props、state 或 DOM 实行修改

  • 要求小心的是,render 方法重回的是杜撰DOM。

    渲染达成之后,大家也许供给对DOM做1些操作,比如截屏、上报日志、可能起头化iScroll等第一方非React插件,能够在
    componentDidMount() 方法中做那个事情。当然,你也能够在那么些法子里通过
    this.getDOMNode()
    方法获得最后生成DOM节点,然后对DOM节点做爱做的事务,但需求注意做好安全措施,不要缓存已经变更的DOM节点,因为这个DOM节点随时可能被交流掉,所以应该在历次用的时候去读取。

STL中的string

string是STL的字符串类型,在C语言中,大家常见用char *或者char[]字符数组来代表字符串。C++的string和C语言的char *有啥分别吧?

  • string是2个类,char *是指向字符的指针
  • string封装了char *,管理这一个字符串,是一个char *项指标容器
  • string毫无考虑内部存款和储蓄器释放和数组越界
  • string提供了1些列的字符串操作函数

  二、更新

  管理 5

  组件被早先化完毕后,它的状态会趁着用户的操作、时间的推移、数据更新而发出变化,变化的进度是组件的翻新。

  解析:

  1. 当组件已经被实例化后,使用者调用 setProps()
    方法修改组件的数目时,组件的 component威尔ReceiveProps()
    方法会被调用,在此处,你能够对表面传入的数额开始展览一些预处理,比如从props中读取数据写入state。
  2. 私下认可景况下,组件在 setState()
    之后,React会遍历这一个组件的全数子组件,举办“灌水”,将props从上到下一层壹层传下去,并每种实施更新操作。有时候,props产生了变动,但组件和子组件并不会因为那个props的生成而发生变化,打个假使,你有贰个表单组件,你想要修改表单的name,同时您可见确信那个name不会对组件的渲染产生其余影响,那么您能够一贯在shouldComponentUpdate()这些方法里return
    false来终止后续表现。那样就可见幸免无效的杜撰DOM相比了,对质量会有引人注目提高。

  3. 零件在革新前,React会执行componentWillUpdate()
    方法,那么些主意类似于前方看到的
    component威尔Mount()方法,唯1不一致的地点只是其一艺术在履行的时候组件是早已渲染过的。供给专注的是,不得以在那些方法中修改props或state,假使要修改,应当在
    component威尔ReceiveProps() 中期维修改。

  4. 接下来是渲染,React会拿本次回来的杜撰DOM和缓存中的虚拟DOM实行自己检查自纠,找出【最小修改点】,然后替换。
  5. 创新完结后,React会调用组件的componentDidUpdate
    方法,这些措施类似于前方 componentDidMount
    方法,你照样能够在此处能够因此 this.getDOMNode()
    方法赢得最终的DOM节点。

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;
}

  三、销毁

字符串的遍历

字符串的遍历,有二种遍历的章程

  • 数组格局遍历,通过[]操作符遍历 (不会抛出13分)
  • 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方法格局,有2个深入人心的不等

  • 数组形式,固然出现越界或许其余错误,不会抛出特别,程序直接终端。
  • at()方法遍历,出现越界或此外错误,会抛出越发,程序能够拍卖万分。

迭代器其实能够当作是1个字符的指针,上个例子中string::iterator it = str.begin()就是概念一个string类型的迭代器,指向str的率先次地方。*it就表示近年来的字符。注意str.end()意味着字符串最终2个字符的末端二个任务。如若it == str.end()就象征早已遍历到终点了。

  管理 6

  • componentWillUnmount
    除了擦屁股什么也做不了。你能够在那么些点子中销毁非React组件注册的事件、插入的节点,只怕部分定时器之类。这么些进度或然不难出错,比如绑定了轩然大波却没销毁,那么些React可帮不了你,你自个儿约的炮,含着泪也要打完。

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;
}

扩展 

  React可以用1套代码同时运营在浏览器和node里,而且能够以原生App的架势运营在iOS和Android系统中,即怀有了web迭代急速的风味,又富有原生App的经验。那几个姿势叫做
React-Native。能够见见React-Native也是卓越吃香——因为React-Native能够使React的价值最大化,那一个股票总值是什么样啊——对事情以来,意味着不需求为了做终端版本就招聘和前端等量人力的顶峰开发,同时期表大家成为全栈工程师有了八个捷径。

  明白iOS开发的同桌都知道,水果公司对运用上架的甄别成效实在令人无力作弄,很多团社团上一个本子还没审核截止,下多少个版本就已经办好了。而React-Native协助从网络拉取JS,那样iOS应用也能够像web1样达成高效迭代了。

string的拼接

string为大家提供了三种字符串拼接格局,一种是重写了 +
操作符,大家可以直接将连个字符串相加,类似于java的语法。另1种是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;
}

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;
}

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;
}

string算法相关

时下大面积的string的算法是深浅写转换。1般选取函数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;
}

STL中的vector容器

vector是将元素放到动态数组中加以管理的器皿。vector容器能够随机存取成分,相当于说援救[]运算符和at情势存取。

  • vector在尾巴部分添加或许移除成分非常的慢,在中间操作格外耗费时间,因为它供给活动元素

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;
}

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;
}

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;
}

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;
}

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;
}

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;
}

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;
}

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;
}

STL中的queue队列容器

队列是1种数据结构,具备队头和队尾。常见的有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();
    }

}

STL中的list容器

list容器具有如下特征:

  • 能够在头顶和尾巴插入和删除成分
  • 不可能自由走访成分,也正是迭代器只好只好++,无法2回性跳转

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;
}

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;
}

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;

}

STL中的set和multiset集合容器

set是二个会师,Objective-C言语中也有成团的概念。C++中的集合与OC中的集合也有两样的地点。

  • C++的set容器,当中涵盖的成分是唯一的,而且是有序的。
  • C++的set容器,是鲁人持竿顺序插入的,不能在钦定地点插入。
  • C++的set容器,其结构是红黑二叉树,插入数据的频率比vector快

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());
    }

}

日常数据类型的排序

set容器是雷打不动的聚众,私下认可的顺序是从小到大的。

创立集合的方法:

  • set<int>始建私下认可的从小到大的int类型的晤面
  • set<int,less<int>>创设1个从小打到大的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 << " ";
    }
}

自定义对象的排序

下面,大家见到了基础的数据类型的聚众,是足以排序的。可是,如果集合里面放的是差别常常的自定义对象,该怎么知足set有序的性状呢?

由此地方的例子,大家知晓,基础数据类型的set是不变的重要原因是greater和less仿函数。所以,自定义对象的稳步也是透过大家自定义仿函数来担保的。

仿函数,之所以叫仿函数,是因为它跟函数很像,但并不是七个函数。它的结果如下,只要我们落到实处了这么些仿函数,我们也得以对自定义对象进行排序。

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

    }
};

上边,大家自定义3个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;
    }

}

pair类型的重回值

pair类型,就像于Swift语言中的“元组”的定义,这些类型涵盖了多少个数据类型,在函数再次来到的时候,能够同时重临多少个值。

我们来看一下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;
    }
}

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; 
}

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);
    }
}

STL中的map和multimap映射容器

map和multimap是3个键值映射的容器。map中的键值对都是唯壹的,不过multimap中二个键足以对应多少个值。

  • map和multimap是关联式容器,键值成对存在
  • map和multimap是红黑变体的平衡2叉树结构
  • map只援助唯一的键值对,集合中的成分是遵从一定的顺序排列的
  • multimap中的键可以出现行反革命复
  • map和multimap的因素插入进程是根据顺序插入的

map元素的增加和删除改查

map成分的插入,有三种艺术:

  1. 调用insert函数插入pair类型的键值对
  2. 直白使用[]来对键进行复制,类似于Objective-C中的NSMutableDictionary赋值1样。

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;
}

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;
}

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++的沙盘个性来落成。需求小心:

  • 容器缓存了节点,节点类要确定保障匡助拷贝(不然出现浅拷贝难题,导致崩溃)
  • 容器中的1般节点类,必要提供拷贝构造函数,视同一律载等号操作符(用来赋值)
  • 容器在插入成分时,会活动进行成分的正片。

针对容器,容器之间也支撑拷贝。所以需求留意:

  • 除却queue和stack外,每个容器都提供了可再次来到迭代器的函数,运用重回的跌打器就足以访问成分
  • 壹般STL不会抛出10分,必要使用者确认保障传入正确的参数
  • 每一种容器都提供了一个暗中同意构造函数和2个默许拷贝构造函数

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 指向的空间 调用析构函数

运转后,打字与印刷出结果后并报错。报错原因是同3个内部存款和储蓄器空间被放飞了三遍,导致的垮台。其根本原因是,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;
}

STL容器的相比

STL提供了广大器皿,种种容器有其自己的特色,大家该如何采纳它们啊?

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

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注