c++声明周期管理


#define  _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <stdexcept>
#include "string"
#include <iomanip>
using namespace std;

/*
    有关构造函数
    1、构造函数定义及调用
    1)C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数;
    2)构造函数在定义时可以有参数;
    3)没有任何返回类型的声明。
    2、构造函数的调用
    自动调用:一般情况下C++编译器会 自动调用 构造函数
    手动调用:在一些情况下则需要手工调用构造函数

    有关析构函数
    3、析构函数定义及调用
    1)C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数
    语法:~ClassName()
    2)析构函数没有参数也没有任何返回类型的声明
    3)析构函数在对象销毁时自动被调用
    4)析构函数调用机制
    C++编译器自动调用
    */
#if 0

class Test
{
public:
    Test()//无参数构造函数
    {
        cout << "我是构造函数 被执行了" << endl;
        p = (char *)malloc(100);
        strcpy(p, "hello world");
        a = 10;
    }
    void print()
    {
        cout << p << endl;
        cout << a << endl;
    }

    ~Test()//析构函数
    {
        if (p != NULL)
        {
            free(p);
        }
        cout << "我是析构函数 被调用了" << endl;
    }

private:
    int a;
    char *p;
protected:
};


//给对象搭建一个舞台,研究对象的行为
//注意对象的生命周期,只有生命周期结束的时候,才会调用析构函数
void test()
{
    //先创建的对象后释放
    Test t1;
    t1.print();
    printf("分隔符\n");

    Test t2;
    t2.print();
}

#endif

/*
    构造函数的分类
    */
#if 0

class Test
{
public:
    //1、无参数构造函数
    Test()
    {
        m_a = 0;
        m_b = 0;
        cout << "我是构造函数 被执行了" << endl;
    }
    //2、有参数构造函数
    Test(int a)
    {
        cout << "我是一个参数的构造函数" << "a = " << a << endl;
        m_a = a;
        m_b = 0;
    }

    Test(int a, int b) //有参数构造函数   //3种方法
    {
        m_a = a;
        m_b = b;
        cout << "有参数构造函数" << endl;
    }

    //3、赋值构造函数(copy构造函数)
    Test(const Test& obj)
    {
        cout << "copy构造函数也是构造函数 " << endl;
    }

    ~Test()//析构函数
    {
        cout << "我是析构函数 被调用了" << endl;
    }
public:
    void printT()
    {
        cout << "普通成员函数" << endl;
    }
private:
    int m_a;
    int m_b;
protected:
};
void test()
{

    {
        //调用无参数构造函数
        Test t1;

        //Test t1();// 错误的写法 warning C4930: “Test t1(void)”: 未调用原型函数(是否是有意用变量定义的?)
        t1.printT();//使用的时候, Test t1()会出错
        cout << "hello..." << endl;
    }

    //调用 调用有参数构造函数 3种方法
    //1括号法
    Test t1(1, 2);//调用参数构造函数  c++编译器 自动的 调用构造函数

    //2 =号法   (3, 4) 是一个逗号表达式,此时需要注意类中仅有一个参数的成员函数
    Test t2 = (3, 4);       //   = c+对等号符 功能增强  c++编译器 自动的 调用构造函数
    Test t3 = (3, 4, 5);    //调试看
    Test t4 = 33;           //调试看

    //3 手动调用 构造函数 显示的调用  直接调用构造函数会产生匿名对象
    //只会调用1次构造函数
    //类名 对象名 = 构造函数名(11, 22)
    Test t5 = Test(11, 22);//会产生匿名对象,此时匿名对象转正,不会被析构(匿名对象的去和留问题)直接调用有参构造函数完成t5对象的初始化 

    t1 = Test(11, 22);    //会产生匿名对象,此时匿名对象被析构
    t1 = t5;//把t5 copy 给 t1 是赋值操作

    /*
    Test t5 = Test(11, 22)   //对象的初始化
    t1 = t5;                 //对象的赋值
    两条语句的 = 是不同的概念
    对象的初始化 和对象的赋值 是两个不同的概念
    */
}
#endif

/*
    copy构造函数的调用  时机1 时机2
    */

#if 0
class Test4
{
public:
    Test4()  //无参数构造函数
    {
        m_a = 0;
        m_b = 0;
        cout << "无参数构造函数" << endl;
    }

    Test4(int a)
    {
        m_a = a;
        m_b = 0;
    }

    Test4(int a, int b) //有参数构造函数   //3种方法
    {
        m_a = a;
        m_b = b;
        cout << "有参数构造函数" << endl;
    }

    //赋值构造函数 (copy构造函数)  作用完成对象的初始化
    Test4(const Test4& obj)
    {
        cout << "赋值构造函数 " << endl;
        //注释掉 会乱码
#if 1
        m_a = obj.m_a + 100;
        m_b = obj.m_b + 100;

#endif
    }

public:
    void printT()
    {
        cout << "普通成员函数" << endl;
        cout << "m_a = " << m_a << ", m_b = " << m_b << endl;
    }
private:
    int m_a;
    int m_b;
};

void test()
{
    //赋值构造函数 用一个对象初始化另一个对象
    Test4 t1(10, 12);
    Test4 t0(0, 2);

    //赋值操作 和 初始化是两个不同的概念

    //赋值 = 操作  不会调用拷贝构造函数
    t0 = t1;        //用t1 给 t0赋值    

    //第一种 调用拷贝构造函数的时机
    Test4 t2 = t1;  //用t1来初始化t2,调用t2这个对象的拷贝构造函数
    t2.printT();

    //第二种 调用拷贝构造函数的时机
    Test4 t3(t1);   //用t1来初始化t2
    t3.printT();
}

#endif

/*
    赋值构造函数的调用  时机3
    */
#if 0
class Location
{
public:
    //构造函数
    Location(int xx = 0, int yy = 0)
    {
        X = xx;
        Y = yy;
        cout << "Constructor Object." << endl;
    }
    //拷贝构造函数  完成对象的初始化
    Location(const Location &obj)
    {
        X = obj.X;
        Y = obj.Y;
    }

    ~Location()
    {
        cout << X << "," << Y << " Object destroyed." << endl;

    }
    int GetX()
    {
        return X;
    }
    int GetY()
    {
        return Y;
    }
private:
    int X, Y;
};

//第三种 调用拷贝构造函数的时机
void f(Location p)  //参数是一个元素
{
    cout << p.GetX() << endl;
}

void test()
{
    Location a(1, 2);
    Location b = a;     //第一种 调用拷贝构造函数的时机
    /*
        实参b去初始化形参p,c++编译器会调用形参 p 这个对象的copy构造函数,
        由于p是局部变量,函数f()运行完毕,p对象的生命周期结束,会调用
        p对象的析构函数。

        先创建的对象后释放
        */
    //b实参去初始化形参p,会调用copy构造函数    
    f(b);           //第3种时机
    cout << "b对象已经初始化完毕" << endl;

}
#endif

/*
    赋值构造函数的调用  时机4
    */
#if 0
class Location
{
public:
    //构造函数
    Location(int xx = 0, int yy = 0)
    {
        X = xx;
        Y = yy;
        cout << "Constructor Object." << endl;
    }
    //拷贝构造函数  完成对象的初始化
    Location(const Location &obj)
    {
        X = obj.X;
        Y = obj.Y;
    }

    ~Location()
    {
        cout << X << "," << Y << " Object destroyed." << endl;

    }
    int  GetX() { return X; }       int GetY() { return Y; }
private:
    int X, Y;
};

//第4种 调用拷贝构造函数的时机

/*
    g()函数 返回一个元素
    结论 1:函数的返回值是一个元素(复杂类型的),返回一个新的匿名对象(所以会调用匿名对象
    的 copy构造函数)。
    结论 2:有关匿名对象的去和留
    如果用匿名对象 初始化 另一个同类型的对象,匿名对象转化为有名对象,匿名对象不会被析构掉
    如果用匿名对象 赋值给 另一个同类型的对象,匿名对象被析构
    */

Location g()
{
    Location A(11, 22);//执行对象A的构造函数
    return A;
    /*
    return A;
    这条语句  首先会创建一个匿名对象,用对象A初始化匿名对象,执行匿名对象的copy构造函数,
    然后对象A的生命周期结束了,会执行对象A的析构函数。g()返回一个匿名对象.
    */
}

void objplay2()
{
    g();//g()返回一个匿名对象,如果匿名对象没人接,则会执行匿名对象的析构函数。
}

void objplay3()
{
    //用匿名对象初始化m,此时c++编译器直接把匿名对象转成m(扶正),从匿名转成有名对象了m
    Location m = g();
    printf("匿名对象,被扶正,不会被析构掉\n");
    cout << m.GetX() << endl;
}

void objplay4()
{

    Location m2(1, 2);
    m2 = g();//用匿名对象 赋值给 m2
    printf("因为用匿名对象 = m2, 匿名对象被析构\n");
    cout << m2.GetX() << endl;
}

void objplay5()
{
    //用匿名对象 赋值给 m2
    Location m1(1, 2);
    m1 = Location(33, 44);  //赋值操作   Location(33, 44)会产生匿名对象,匿名对象被析构

    Location m2 = Location(55, 66);//初始化操作 Location(33, 44)会产生匿名对象,匿名对象转正,不会被析构
}

void objplay6()
{
    Location(77, 88);  //直接调用构造函数,会产生匿名对象,临时匿名对象的生命周期  
}

void test()
{
    //objplay2();
    //objplay3();
    //objplay4();
    objplay5();
    //objplay6();

}
#endif

/*
    当类中定义了  拷贝   构造函数时, c++编译器不会提供无参数  构造函数
    当类中定义了  有参数 构造函数时,c++编译器不会提供无参数  构造函数

    在定义类时, 只要你写了构造函数,则必须要用
    */
#if 0
class Test
{
public:

    //Test(const Test& obj) //copy构造函数 作用: 用一个对象初始化另外一个对象
    //{
    //  a = obj.a + 100;
    //  b = obj.b + 100;
    //}

    /*  Test(int _a, int _b)
       {
       ;
       }*/
    /*Test()
    {

    }*/

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

protected:
private:
    int a;
    int b;
};
void test()
{
    Test t1; //调用无参构造函数
    cout << "hello..." << endl;
}

#endif 

#if 0
class Name
{
public:
    //构造函数
    Name(const char *myp)
    {
        m_len = strlen(myp);
        m_p = (char *)malloc(m_len + 1);
        strcpy(m_p, myp);
    }

    //Name t2 = t1;
    //解决方案:手工的编写拷贝构造函数 使用深copy
    Name(const Name& obj)
    {
        m_len = obj.m_len;
        m_p = (char *)malloc(m_len + 1);
        strcpy(m_p, obj.m_p);
    }


    ~Name()
    {
        if (m_p != NULL)
        {
            free(m_p);
            m_p = NULL;//释放完内存要置空
            m_len = 0;
        }
    }

protected:
private:
    char *m_p;
    int m_len;

};
void test()
{
    Name t1("hello world"); //调用无参构造函数
    //Name t2 = t1;//c++编译器提供的默认拷贝构造函数 是 浅拷贝,多次释放 同一块内存

    //----------------------------------------------------------
    Name t3("hhhh");
    t3 = t1;    // = 操作 把对象1的属性 拷贝给对象3  是浅拷贝,多次释放 同一块内存,还有一块内存未释放
    //需要解决重载 = 操作符
    cout << "hello..." << endl;
}

#endif 

/*
    构造函数的初始化列表
    */
#if 0

class A
{
public:
    A(int _a)
    {
        a = _a;
        cout << "构造函数" << " a = " << a << endl;
    }

    ~A()
    {
        cout << "析构函数" << " a = " << a << endl;
    }
protected:
private:
    int a;
};

/*
    1、构造函数的初始化列表
    解决:在B类中 组合了一个 A类对象(A类设计了构造函数),
    根据构造函数的调用规则,设计A的构造函数,必须要用;没有机会初始化A。
    新的语法 Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
    2、调用顺序
    先执行组合对象的构造函数,如果组合对象有多个,按照定义顺序,而不是按照初始化列表的顺序

    3、析构函数:和构造函数的调用顺序相反
    4、初始化列表 用来给const 属性赋值
    */
class B
{
public:
    B(int _b1, int _b2) :a1(1), a2(2), c(0)
    {

    }
    B(int _b1, int _b2, int m, int n) :a1(m), a2(n), c(0)
    {
        b1 = _b1;
        b2 = _b2;
        cout << "B的构造函数" << endl;
    }

    ~B()
    {
        cout << "B的析构函数" << endl;
    }
protected:
private:
    int b1;
    int b2;
    A a2;   //组合对象
    A a1;   //
    const int c;
};

void test()
{
    A a1(10);
    //B ojbB(1, 2);
    //1 参数传递 
    B ojbB2(1, 2, 3, 4);
    //2 调用顺序

    cout << "hello..." << endl;
}

#endif

/*
    构造中调用构造是危险的行为
    */

#if 0
class MyTest
{
public:
    MyTest(int a, int b, int c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
    }

    MyTest(int a, int b)
    {
        this->a = a;
        this->b = b;

        MyTest(a, b, 100); //直接调用构造函数,产生新的匿名对象,没人接,会直接调用匿名对象的析构函数。 跟t1对象没有半毛线关系
    }
    ~MyTest()
    {
        printf("MyTest~:%d, %d, %d\n", a, b, c);
    }

protected:
private:
    int a;
    int b;
    int c;

public:
    int getC() const { return c; }
    void setC(int val) { c = val; }
};

void test()
{
    MyTest t1(1, 2);
    printf("-----c:%d", t1.getC()); //请问c的值是?
}

#endif

/*
    new和delete的基本使用,是c++的操作符
    */
#if 0
class Test
{
public:
    Test(int _a)
    {
        a = _a;
        cout << "构造函数执行" << endl;
    }
    ~Test()
    {
        cout << "析构函数执行" << endl;
    }

protected:
private:
    int a;
};

void test()
{
    int *p = new int;
    *p = 20;
    printf("*p = %d\n", *p);
    delete p;

    int *p1 = new int(21);//定义并初始化
    printf("*p1 = %d\n", *p1);
    delete p1;

    int *pArray = new int[10];
    pArray[0] = 10;
    delete[]pArray;

    char *str = new char[25];
    strcpy(str, "12345");
    printf("str = %s\n", str);
    delete[]str;

    //对象的操作
    Test *pT1 = (Test *)malloc(sizeof(Test));
    free(pT1);

    Test *pT2 = new Test(10);   //new能执行类型构造函数
    delete(pT2);                //delete操作符 能执行类的析构函数

    printf("---------------------------\n");
    //深入分析  

    //malloc  用delete释放
    Test *pT3 = (Test *)malloc(sizeof(Test));
    delete(pT3);
    //new 用free释放
    Test *pT4 = new Test(10);   //new能执行类型构造函数
    free(pT4);                //delete操作符 能执行类的析构函数
    /*
        new不但可以分配内存,还可以初始化对象
        malloc不会调用类的构造函数
        Free不会调用类的析构函数

        */

}

#endif

/*
    static 静态成员函数 和静态成员变量
    */

#if 0

class BB
{
    int c0;//默认是private

public:
    static int d; //使用公有静态数据成员

    void printC()//成员函数访问静态成员变量
    {
        cout << "c = " << c << endl;
    }

    void addC()
    {
        c = c + 1;
    }
    //静态成员函数
    //c++编译器无法确认是b1.a   b2.a   b3.a 
    static void getC()
    {
        cout << "c = " << c << endl;
        //cout << "a = " << a << endl;//error C2597: 对非静态成员“BB::a”的非法引用 ,因为静态成员变量属于整个类的,分不清楚普通成员变量是哪个具体对象的属性
        /*
            静态函数中 不能使用 普通成员变量 普通成员函数 .
            静态成员函数数冠以关键字static
            静态成员函数提供不依赖于类数据结构的共同操作,它没有this指针
            在类外调用静态成员函数用 “类名 :: ”作限定词,或通过对象调用
            静态成员函数属于整个类的
            static修饰的变量,是属于类,所有的对象都能共享使用
            */
    }
protected:

private:
    int a;
    int b;
    static int c;//静态成员变量
};

/*
如果不写静态函数的定义,当不用类定义对象时候,编译不会报错,只用定义类的对象时候
不写的话才会报错
*/
int BB::c = 10;  //必须在类外声明与定义静态数据成员
int BB::d = 11;
//static 修饰的变量,属于类,所有的对象都能共享用
void test()
{
    BB b1, b2, b3;
    b1.printC();    //10
    b2.addC();      //11
    b3.printC();    //11
    b3.getC();

    //访问公有静态成员方法1
    cout << "BB::d = " << BB::d << endl;
    //访问公有静态成员方法2
    cout << "b1.d =  " << b1.d << endl;

    //静态成员函数 调用方法
    b3.getC();  //用对象
    BB::getC(); //类::

}
#endif

/*
    1)C++类对象中的成员变量和成员函数是分开存储的
    成员变量:
    普通成员变量:存储于对象中,与struct变量有相同的内存布局和字节对齐方式
    静态成员变量:存储于全局数据区中
    成员函数:存储于代码段中。

    1、C++类对象中的成员变量和成员函数是分开存储的。C语言中的内存四区模型仍然有效!
    2、C++中类的普通成员函数都隐式包含一个指向当前对象的this指针。
    3、静态成员函数、成员变量属于类
    静态成员函数与普通成员函数的区别
    静态成员函数不包含指向具体对象的指针
    普通成员函数包含一个指向具体对象的指针

    */
#if 0
class C1
{
public:
    int i;  //4
    int j; //4
    int k;  //4
protected:
private:
}; //12

class C2
{
public:
    int i;
    int j;
    int k;

    static int m; //4
public:
    int getK() const { return k; } //4
    void setK(int val) { k = val; }  //4

protected:
private:
}; //24 16 12(铁钉的不对)

struct S1
{
    int i;
    int j;
    int k;
}; //

struct S2
{
    int i;
    int j;
    int k;
    static int m;
}; //

void test()
{
    printf("c1:%d \n", sizeof(C1));
    printf("c2:%d \n", sizeof(C2));
    printf("s1:%d \n", sizeof(S1));
    printf("s2:%d \n", sizeof(S2));
}
#endif

//this指针和const
#if 0

class A
{
public:
    A(int a, int b)// ====》A( A*pThis, int a, int b);
    {
        this->a = a;
        this->b = b;
    }

    void printA()
    {
        cout << "a = " << a << endl;
        cout << "b = " << this->b << endl;
    }

    void opVar1(int a, int b)//==> void opVar1(A * const pThis, int a, int b)
    {
        a = 100;
        this->a = 80;
    }
    /*
    const写在什么位置 没有关系
    const修饰的是this指针所指向的内存空间,修饰的是this指针
    指针变量与指针所指向的内存空间 是两个不同的概念
    */
    void opVar2(int a, int b) const //==>void opVar1(const A * const pThis,int a, int b)
    {
        a = 100;
        //this->a = 80;//err
    }
protected:
private:
    int a;
    int b;
};
void test()
{
    A a1(1, 2);
    a1.printA();//==> printA(&a1);

}

#endif

/*
    成员函数与全局函数的转换
    */
#if 0
class Test
{
public:

    Test(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
    }
    /*
    t4 = t1.TestAdd(t2);
    返回一个引用 相当于返回自身
    返回t1这个元素  this 就是&t1, this 是常指针
    */
    Test TestAdd(Test &obj)//函数返回元素
    {
        Test tmp(this->a + obj.a, this->b + obj.b);
        return tmp;//返回一个匿名对象
    }

    // t1.TestAdd(t2);函数返回引用
    void TestAdd2(Test &obj)
    {
        this->a = this->a + obj.a;
        this->b = this->b + obj.b;
        //return *this;//把 *(&t1)  又回到了 t1元素
    }


    ~Test()
    {
        cout << "a:" << a << " b: " << b;
        cout << "析构函数自动被调用" << endl;
    }

    void printT()
    {
        cout << "a:" << a << " b: " << b << endl;
    }
public:
    int a;
    int b;
};

//成员函数 转成全局函数  多了一个参数
void printT(Test *pT)
{
    cout << "a" << pT->a << "b" << pT->b << endl;
}

//全局函数方法
//全局函数 转成 成员函数  少了一个参数 
Test TestAdd(Test &t1, Test &t2)
{
    Test tmp;
    tmp.a = t1.a + t2.a;
    tmp.b = t1.b + t2.b;
    return tmp;
}


void test()
{
    Test t1(1, 2);
    Test t2(3, 4);
    Test t3;//偷懒的写法是在构造函数中直接使用默认参数
    //全局函数方法
    t3 = TestAdd(t1, t2);
    t3.printT();
    //成员函数法
    {
        //先把测试案例写出来 t4 = t1 + t2
        Test t4 = t1.TestAdd(t2);//匿名对象直接转化为t4
        t4.printT();
        Test t5;
        t5 = t1.TestAdd(t2);//匿名对象复制给t5

        //t1 = t1+ t2
        t1.TestAdd2(t2);
        t1.printT();
    }
}


#endif

/*
    友元函数和友元类
    */
#if 0

class A
{
public:
    friend class B;//B类是A类的好朋友, 在B类中可以访问A类的所有成员

    //1 声明的位置 和 public private没有关系
    //2 函数modifyA 是 类A的好朋友
    friend void modifyA(A *pA, int _a);

    A(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
    }
    int getA()
    {
        return this->a;
    }
private:
    int a;
    int b;
};

//友元函数是全局函数,没有this指针 可以修改类的私有成员 注意函数原型的第一个参数
void modifyA(A *pA, int _a)
{
    pA->a = _a;
}

class B
{
public:
    void Set(int a)
    {
        Aobject.a = a;
    }
    void printB()
    {
        cout << Aobject.a << endl;
    }
private:
    A Aobject;//类A定义
};

//为什么设计友元类函数
// 1.java--->1.class(字节码) ==》反射机制分析1.class 找到类对象。直接修改类的私有属性。。。
//反射机制 成为一种标准。。。。jdk ...sun 做成标准 。。。jdk 的 api函数中有体现 
//AOP
//2 1.cpp===>汇编
// 预编译 编译  连接  生成 。。gcc -E //gcc -s  -
//gcc -o 1.exe 1.c 
// 汇编往会找。。。。很难。。。。
//3 开了一个后门 。。。friend

/*
gcc -E hello.c -o hello.i(预处理)
gcc -S hello.i -o hello.s(编译)
gcc -c hello.s -o hello.o(汇编)
gcc hello.o -o hello(链接)
以上四个步骤,可合成一个步骤

gcc hello.c -o hello(直接编译链接成可执行目标文件)
gcc -c hello.c或gcc -c hello.c -o hello.o(编译生成可重定位目标文件)
*/

void test()
{
    A a1(1, 2);
    cout << a1.getA() << endl;
    modifyA(&a1, 300);
    cout << a1.getA() << endl;

    B b1;
    b1.Set(600);
    b1.printB();

}
#endif

/*
    运算符重载的技术推演
    */
#if 0
class Complex
{
public:

    friend Complex myAdd(Complex &c1, Complex &c2);
    friend Complex operator+(Complex &c1, Complex &c2);
    Complex(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
    }
    void printCom()
    {
        cout << a << " + " << b << "i" << endl;
    }
private:
    int a;
    int b;
};

//1 定义全局函数
Complex myAdd(Complex &c1, Complex &c2)
{
    Complex tmp(c1.a + c2.a, c1.b + c2.b);
    return tmp;
}

//函数名升级
Complex operator+(Complex &c1, Complex &c2)
{
    Complex tmp(c1.a + c2.a, c1.b + c2.b);
    return tmp;
}
/*
    全局函数、类成员函数方法实现运算符重载步骤
    1)要承认操作符重载是一个函数,写出函数名称
    2)根据操作数,写出函数参数
    3)根据业务,完善函数返回值(看函数是返回引用 还是指针 元素),及实现函数业务
    */
void test()
{
    int a = 0, b = 0;
    int c;
    c = a + b;//1 基础数据类型 编译器已经知道了. 如何运算
    Complex c1(1, 2), c2(3, 4);
    Complex c3;
    //2 类 也是一种数据类型  用户自定义数据类型 C++编译器 是不知道如何进行运算
    //c3 = c1 + c2
    //3 c++编译器应该给我们程序员提供一种机制 ... 
    //让自定义数据类型 有机会 进行 运算符操作 ====> 运算符重载机制 

    //4 运算符重载机制

    //步骤1
    Complex c4 = myAdd(c1, c2);
    c4.printCom();

    //步骤2 Complex c4 = c1 + c2
    Complex c5 = operator+(c1, c2);
    c5.printCom();

    //步骤3 
    Complex c6 = c1 + c2;
    c6.printCom();
    //总结 运算符重载的本质 是函数调用
}
#endif

/*
    运算符重载 - + -- ++
    */
#if 0
class Complex
{
public:
    //前置++
    friend Complex& operator++(Complex &c1);
    //后置++
    friend Complex operator++(Complex &c1, int);//使用占位符

    //成员函数 重载  -运算符
    Complex operator-(Complex &c2)
    {
        cout << "c2.a" << c2.a << " c2.b" << c2.b << endl;
        //Complex tmp = (this->a - c2.a, this->b - c2.b);//是一个逗号表达式
        Complex tmp(this->a - c2.a, this->b - c2.b);
        return tmp;
    }
    //前置--
    Complex& operator--()
    {
        this->a--;
        this->b--;
        return *this;
    }
    //后置--
    Complex operator--(int)
    {
        Complex tmp = *this;
        this->a--;
        this->b--;
        return tmp;
    }

    Complex(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
    }
    void printCom()
    {
        cout << a << " + " << b << "i" << endl;
    }

private:
    int a;
    int b;
};

//前置++ 全局函数
Complex& operator++(Complex &c1)
{
    c1.a++;
    c1.b++;
    return c1;
}

//后置++ 全局函数
Complex operator++(Complex &c1, int)
{
    //先使用 后加1
    Complex tmp = c1;
    c1.a++;
    c1.b++;
    return tmp;
}


void test()
{
    Complex c1(1, 2), c2(3, 4);
    //1 成员函数 法 实现 -运算符重载
    //
    //c1.operator-(c2);
    //Complex operator-(Complex &c2);
    Complex c3 = c1 - c2;
    c3.printCom();

    //重载 前置++ 全局函数
    ++c1;
    c1.printCom();

    //重载 前置-- 成员函数
    --c1;
    c1.printCom();

    //重载后置++  全局函数
    c1++;
    c1.printCom();

    //重载后置-- 成员函数
    c1--;
    c1.printCom();
}
#endif

/*
   + - ++ -- << 重载操作
   */
#if 0
class Complex
{
public:
    //friend void operator<<(ostream &out, Complex &obj);
    friend ostream& operator<<(ostream &out, Complex &obj);
    Complex(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
    }
    //c3 = c1 + c2
    Complex operator+(Complex &obj)
    {
        Complex tmp(this->a + obj.a, this->b + obj.b);
        return tmp;
    }

    //c1 = c1 - c2
    Complex& operator-(Complex &obj)
    {
        this->a = this->a - obj.a;
        this->b = this->b - obj.b;
        return *this;
    }
    //++c1 使用前先将自身加1
    Complex& operator++()
    {
        this->a++;
        this->b++;
        return *this;
    }

    //c1++ 
    Complex operator++(int)
    {
        Complex tmp = *this;
        this->a++;
        this->b++;
        return tmp;
    }

    //--c1
    Complex& operator--()
    {
        this->a--;
        this->b--;
        return *this;
    }

    //c1--
    Complex operator--(int)//使用占位符,c++编译器则能够区分是后置--
    {
        Complex tmp = *this;
        this->a--;
        this->b--;
        return tmp;
    }
    void print()
    {
        cout << a << " + " << b << "i" << endl;
    }

private:
    int a;
    int b;
};

//void operator<<(ostream &out, Complex &obj)
//{
//  out << obj.a << " + " << obj.b << endl;
//}

// << 必须用友元函数 因为取不到 ostream 类的实现
ostream& operator<<(ostream &out, Complex &obj)
{
    out << obj.a << " + " << obj.b << endl;
    return out;
}

void test()
{
    Complex c1(1, 2), c2(3, 4);

    Complex c3 = c1 + c2;
    c3.print();

    c1 = c1 - c2;
    c1.print();

    ++c1;
    c1.print();

    c1++;
    c1.print();

    --c1;
    c1.print();

    c1--;
    c1.print();

    int a = 10;
    cout << a << endl;

    //重载<<  只能用友元函数
    //2 ostream 类中 添加 成员函数 .operator<<
    //ostream
    //cout.operator<<(c1);
    cout << c1;
    //函数返回值当左值,需要返回一个引用
    //cout.operator<<(c1).operator<<("hello")
    //void.operator<<("aaddddd");
    //<< 从左到右
    cout << c1 << "hello" << endl;
}

#endif

/*
    重载 = 操作 (赋值)
    */
#if 0
class Name
{
public:
    Name(const char *p)
    {
        this->m_len = strlen(p) + 1;
        this->m_p = new char[m_len];
        strcpy(m_p, p);
    }

    //Name n2 = n1; 深copy
    Name(const Name& obj)
    {
        m_len = obj.m_len;
        m_p = new char[m_len];
        strcpy(m_p, obj.m_p);
    }

    //n3 = n1;  赋值操作是浅拷贝,需要重载=
    Name& operator=(Name& obj)
    {
        //1 释放 n3 旧的内存 
        if (this->m_p != NULL)
        {
            delete[]m_p;
            m_p = NULL;
            m_len = 0;
        }
        //2 重新根据n1 给n3分配内存
        m_len = obj.m_len;
        m_p = new char[m_len];
        //3 复制
        strcpy(m_p, obj.m_p);
        return *this;
    }

    ~Name()
    {
        if (m_p != NULL)
        {
            delete[]m_p;
            m_p = NULL;
            m_len = 0;
        }
    }
private:
    int m_len;
    char* m_p;
};

void test()
{
    Name n1("hello");
    Name n2 = n1;   //初始化  调用拷贝构造函数

    Name n3("haha");
    //n3.operator(n1)
    n3 = n1;        //赋值操作也是浅拷贝, 需要 =操作符重载

    Name n4("4444");
    Name n5("55555");
    Name n6("666666");
    //= 自右向左
    //n5.operator(n6)
    //n4 = void
    //void.operator(n5)

    n4 = n5 = n6;//链式编程
}
#endif

/*
   重载()
   */
#if 0
class F
{
public:
    //()重载
    int operator() (int a, int b)
    {
        return a*a + b*b;
    }
};


class F2
{
public:
    int Menfuc(int a, int b)
    {
        return a*a + b*b;
    }
};
void test()
{
    F f;
    // ()重载
    f(2, 4);//要么是成员函数初始化,要么是()重载

    F2 f2;
    //函数调用
    f2.Menfuc(2, 4);
}
#endif

/*
    理论知识:
    1)&&和||是C++中非常特殊的操作符
    2)&&和||内置实现了短路规则
    3)操作符重载是靠函数重载来完成的
    4)操作数作为函数参数传递
    5)C++的函数参数都会被求值,无法实现短路规则

    结论:
    &&和||可以实现运算符重载,但无法实现短路规则

    */
#if 0
class Test
{
    int i;
public:
    Test(int i)
    {
        this->i = i;
    }

    Test operator+ (const Test& obj)
    {
        Test ret(0);

        cout << "执行+号重载函数" << endl;
        ret.i = i + obj.i;
        return ret;
    }

    // 重载 &&
    bool operator&& (const Test& obj)
    {
        cout << "执行&&重载函数" << endl;
        return i && obj.i;
    }
};
// && 从左向右
void test()
{
    int a1 = 0;
    int a2 = 1;

    cout << "注意:&&操作符的结合顺序是从左向右" << endl;

    //短路规则 a1 为0,则a1 + a2 不执行
    if (a1 && (a1 + a2))
    {
        cout << "有一个是假,则不在执行下一个表达式的计算" << endl;
    }

    //测试
    Test t1 = 0;
    Test t2 = 1;

    //if( t1 && (t1 + t2)  )
    //t1  && t1.operator+(t2)
    // t1.operator&&(  t1.operator+(t2) )   

    //1 && || 重载他们 不会产生短路效果
    if ((t1 + t2) && t1)
    {
        //t1.operator+(t2) && t1;
        //(t1.operator+(t2)).operator&&(t1);

        cout << "两个函数都被执行了,而且是先执行了+" << endl;
    }

    //2 && 运算符的结合性
    // 两个逻辑与运算符  在一块的时候, 才去谈 运算符的结合性
    // 从左到右    (t1 + t2) && t1 ; 运算结果 && t2)
    //if(  (t1 + t2) && t1 && t2)
    {
        //t1.operator+(t2) && t1;
        //(t1.operator+(t2)).operator&&(t1);

        cout << "两个函数都被执行了,而且是先执行了+" << endl;
    }
}
#endif

/*
    继承的基本语法
    */

#if 0
class Parent
{
public:
    void print()
    {
        a = 0;
        b = 0;
        cout << "a" << a << endl;
        cout << "b" << b << endl;
    }
    int b;
protected:
private:
    int a;
};

class Child : public Parent
{
public:
protected:
private:
    int c;
};

void test()
{
    Child c1;
    c1.b = 10;
    //c1.c = 10;
    c1.print();
}


#endif

/*
    单个类的访问控制
    public:     修饰的成员变量 方法 在类的内部、类的外部都能使用
    protected:  修饰的成员变量 方法 在类的内部使用, 在继承的子类中使用;其他类的外部不能使用
    private:    修饰的成员变量 方法 只能在类的内部使用, 不能在类的外部中使用

    派生类访问控制结论:
    1 protected 关键字 修饰的成员变量和成员函数, 是为了在家族中使用,是为了继承
    2 项目中开发一般用public
    */
#if 0
class Parent
{
public:
    int a;
protected:
    int b;
private:
    int c;
};

//public 控制
class Child : public Parent
{
public:
    void useVar()
    {
        a = 0;//ok
        b = 0;//ok
        //c = 0;//err
    }
protected:
private:
};
//private
class Child2 : private Parent
{
public:
    void useVar()
    {
        a = 0;//ok
        b = 0;//ok
        //c = 0;//err
    }
protected:
private:
};

//protected
class Child3 : protected Parent
{
public:
    void useVar()
    {
        a = 0;//ok
        b = 0;//ok
        //c = 0;//err
    }
protected:
private:
};

/*
    C++中的继承方式(public、private、protected)会影响子类的对外访问属性
    判断某一句话,能否被访问
    1)看调用语句,这句话写在子类的内部、外部
    2)看子类如何从父类继承(public、private、protected)
    3)看父类中的访问级别(public、private、protected)
    */

void test()
{
    Parent t1, t2;
    t1.a = 10;//ok
    //t1.b = 10;//err
    //t1.c = 10;//err

    Child2 c2;
    //c2.a = 10;//err
    //c2.b = 10;//err
    //c2.c = 10;//err

    Child3 c3;
    //c3.a = 10;//err
    //c3.b = 10;//err
    //c3.c = 10;//err
}
#endif

/*
    访问控制原则 综合练习
    */
#if 0
class A
{
private:
    int a;
protected:
    int b;
public:
    int c;

    A()
    {
        a = 0;      b = 0;      c = 0;
    }

    void set(int a, int b, int c)
    {
        this->a = a;        this->b = b;        this->c = c;
    }
};
class B : public A
{
public:
    void print()
    {
        //cout<<"a = "<<a; //err
        cout << "b = " << b; //ok
        cout << "c = " << endl; //ok
    }
};

class C : protected A
{
public:
    void print()
    {
        //cout<<"a = "<<a; //err
        cout << "b = " << b; //ok 
        cout << "c = " << endl; //ok
    }
};

class D : private A
{
public:
    void print()
    {
        //cout<<"a = "<<a;              //err
        cout << "b = " << b << endl;  //ok
        cout << "c = " << c << endl;  //ok 
    }
};

void test()
{
    A aa;
    B bb;
    C cc;
    D dd;

    aa.c = 100;     // ok
    bb.c = 100;     // ok
    //cc.c = 100;   // err
    //dd.c = 100;   // err

    aa.set(1, 2, 3);    //ok
    bb.set(10, 20, 30); //ok
    //cc.set(40, 50, 60); //err 
    //dd.set(70, 80, 90);   //err


    bb.print(); //ok
    cc.print(); //ok
    dd.print(); //ok

}


#endif

/*
    类型兼容性原则
    */
#if 0
/*
    兼容规则中所指的替代包括以下情况:
    子类对象可以当作父类对象使用
    子类对象可以直接 赋值   给父类对象
    子类对象可以直接 初始化   父类对象

    父类指针可以直接指向子类对象
    父类引用可以直接引用子类对象
    */

class Parent
{
public:
    void printP()
    {
        cout << "我是爹。。。" << endl;
    }

    Parent()
    {
        cout << "Parent 构造函数" << endl;
    }

    Parent(const Parent &obj)
    {
        cout << "copy 构造函数" << endl;
    }
private:
    int a;
};

class child : public Parent
{
public:
    void printC()
    {
        cout << "我是儿子" << endl;
    }
protected:
private:
    int c;
};

void howToPrint(Parent *base)
{
    base->printP();//父类的 成员函数
}

void howToPrint2(Parent &base)
{
    base.printP();//父类的 成员函数
}


void test()
{
    Parent p1;
    p1.printP();

    child c1;
    c1.printC();
    c1.printP();

    //赋值兼容性原则
    //1-1 基类指针(引用)指向子类对象
    Parent *p = NULL;
    p = &c1;
    p->printP();


    //1-2 指针做函数参数
    howToPrint(&p1);
    howToPrint(&c1);    //父类指针可以直接指向子类对象

    //1-3引用做函数参数
    howToPrint2(p1);
    howToPrint2(c1);    //父类引用可以直接引用子类对象

    //子类对象 初始化父类对象 子类是一种特殊的父类
    Parent p3 = c1;
    p1 = c1;  //子类对象可以直接 赋值   给父类对象
}

#endif

/*
    继承中的构造和析构

    结论:
    先调用 父类构造函数 在调用子类构造函数
    析构的顺序 和构造相反

    1 子类对象在创建时会首先调用父类的构造函数
    2 父类构造函数执行结束后,执行子类构造函数
    3 当父类的构造函数有参数时,需要在子类的初始化列表中显示调用
    4 析构函数调用的顺序与构造函数相反
    */
#if 0
class Parent
{
public:
    //构造函数,写了就必须使用
    Parent(int a, int b)
    {
        cout << "父类构造函数。。。" << endl;
    }

    ~Parent()
    {
        cout << "父类析构函数。。。" << endl;
    }

    void printP(int a, int b)
    {
        this->a = a;
        this->b = b;
        cout << "我是爹..." << endl;
    }

private:
    int a;
    int b;
};


class child : public Parent
{
public:
    //从父类中继承,必须提供有参构造函数,发现没有机会提供有参构造函数。因此
    //需要使用构造函数的初始化列表
    //child(int c)
    child(int a, int b, int c) :Parent(11, 22)
    {
        this->c = c;
        cout << "子类构造函数。。" << endl;
    }

    ~child()
    {
        cout << "子类析构。。。" << endl;
    }
    void printC()
    {
        cout << "我是儿子" << endl;
    }
protected:
private:
    int c;
};

void playObj()
{
    child c1(1, 2, 3);
}

void test()
{
    Parent p(1, 2);
    playObj();

}
#endif

/*
    继承和组合混搭下的构造和析构
    单步执行 查看调用顺序

    结论:
    先构造父类, 再构造成员变量,最后调用自己
    先析构自己, 在析构成员变量,最后析构父类
    */
#if 0
class Object
{
public:
    Object(int a, int b)
    {
        this->a = a;
        this->b = b;
        cout << "object构造函数 执行 " << "a" << a << " b " << b << endl;
    }
    ~Object()
    {
        cout << "object析构函数 \n";
    }
protected:
    int a;
    int b;

};

class Parent :public Object
{
public:
    //继承了Object类,构造函数,写了就必须使用
    Parent(char *p) :Object(1, 2)
    {
        this->p = p;
        cout << "父类构造函数。。。" << p << endl;
    }

    ~Parent()
    {
        cout << "父类析构函数。。。" << p << endl;
    }

    void printP(int a, int b)
    {
        cout << "我是爹..." << endl;
    }

protected:
    char *p;
};


class child : public Parent
{
public:
    //从父类中继承,必须提供有参构造函数,发现没有机会提供有参构造函数。因此
    //需要使用构造函数的初始化列表
    //child(int c)
    child(char *p) :Parent(p), obj1(3, 4), obj2(5, 6)
    {
        this->myp = p;
        cout << "子类的构造函数。。" << endl;
    }

    ~child()
    {
        cout << "子类析构。。。" << myp << endl;
    }
    void printC()
    {
        cout << "我是儿子" << endl;
    }
protected:
    char *myp;
    Object obj2;
    Object obj1;
};

void test()
{
    child c1("继承测试");
}
#endif

/*
    继承中的同名成员变量、同名成员函数
    1 当子类成员变量与父类成员变量同名时,子类依然从父类继承同名成员
    2 在子类中通过作用域分辨符::进行同名成员区分(在派生类中使用基类
    的同名成员,显式地使用类名限定符)
    3 同名成员存储在内存中的不同位置

    结论:
    基类成员的作用域延伸到所有的派生类
    派生类的重名成员屏蔽基类的同名成员
    派生类屏蔽基类同名成员函数,调用自身的成员函数
    */
#if 0
class A
{
public:
    int a;
    int b;
public:
    void get()
    {
        cout << "b = " << b << endl;
    }

    void print()
    {
        cout << "AAAA" << endl;
    }
protected:
private:
};

class B : public A
{
public:
    int b;
    int c;
    void get_child()
    {
        cout << "b = " << b << endl;
    }

    void print()
    {
        cout << "BBBB" << endl;
    }
protected:
private:
};

void test()
{
    B b1;
    b1.b = 1;
    b1.get_child();
    b1.A::b = 100;//修改父类的b
    b1.B::b = 12; //修改子类的b , 默认情况是B
    b1.get();

    b1.print();
    b1.A::print();//调用父类的同名成员函数

}
#endif

/*
    继承中的static 关键字

    1、基类定义的静态成员,将被所有的派生类共享。
    2、static 关键字修饰的成员变量或者函数 遵守 派生类的访问控制规则
    3、static易犯错误(不但要初始化,更重要的显示的告诉编译器分配内存)
    构造函数默认为 private!!!

    */
#if 0
class A
{
    //注意 类默认下的函数 是私有的
    /*A()
    {
    cout << "A的构造函数" << endl;
    }*/
public:
    A()
    {
        cout << "A的构造函数" << endl;
    }

public:
    static int a;// static 关键字
    int b;

public:
    void get()
    {
        cout << "b = " << b << endl;
    }
    void print()
    {
        cout << "AAAAA " << endl;
    }
};

/*
    不是简单的赋值 更重要的是 要告诉C++编译器, 你要给我分配内存,
    我在继承中用到了a ,不然会报错
    */
int A::a = 100;

class B : private A
{

public:
    int b;
    int c;
public:
    void get_child()
    {
        cout << "b " << b << endl;
        cout << a << endl;
    }
    void print()
    {
        cout << "BBBB " << endl;
    }

protected:
private:
};
void test()
{
    B b1;
    //b1.a = 200;//err a虽然是static,但是私有继承父类
    b1.get_child();

    A a1;
    a1.print();
}

#endif

/*
    多继承的语法
    */
#if 0
class Base1
{
public:
    //构造函数写了,就要用
    Base1(int b1)
    {
        this->b1 = b1;
    }

    void printB1()
    {
        cout << "b1:" << b1 << endl;
    }
protected:
private:
    int b1;
};

class Base2
{
public:
    Base2(int b2)
    {
        this->b2 = b2;
    }

    void printB2()
    {
        cout << "b2:" << b2 << endl;
    }
protected:
private:
    int b2;
};

class B : public Base1, public  Base2
{
public:
    //父类的构造函数写了,就必须用初始化列表初始化
    B(int b1, int b2, int c) :Base1(b1), Base2(b2)
    {
        this->c = c;
        cout << b1 << "  " << b2 << c << endl;
    }

    void printC()
    {
        cout << "c:" << c << endl;
    }
protected:
private:
    int c;
};

void test()
{
    B b1(1, 2, 3);
    b1.printC();
    b1.printB1();
    b1.printB2();
}

#endif

/*
    1、虚继承 让老祖宗的构造函数只执行一次。
    若不加virtual, 则会执行2次老祖宗的构造函数虚继承,只会解决有共同老祖宗的这种问题
    2、子类有两个父类且这两个父类都有相同的成员变量,则加上virtual也不能解决
    结论:
    多继承始终存在问题
    */
#if  0
class B
{
public:
    int b;
    B()
    {
        cout << "老祖宗" << endl;
    }
protected:
private:

};

class B1 : virtual public B
{
public:
    int b1;
};

class B2 : virtual public B
{
public:
    int b2;
};

class C : public B1, public B2
{
public:
    int c;
};

void test()
{
    C  c1;
    c1.b1 = 100;
    c1.b2 = 200;
    c1.c = 300;

    //c1.b = 500;//继承的二义性 ,用虚继承解决此种场景
    c1.B1::b = 500;
    c1.B2::b = 500;
}

#endif

/*
    多继承的另一种场景 用virtual也不能解决
    2、子类有两个父类且这两个父类都有相同的成员变量,则加上virtual也不能解决
    */
#if 0
class B1
{
public:
    int b;
};

class B2
{
public:
    int b;
};

class C : virtual public B1, virtual public B2
{
public:
    int c;
};


void test()
{
    C  c1;

    //c1.b = 500;//继承的二义性 即使加上virtual也不能解决
    //只能这么解决
    c1.B1::b = 500;
    c1.B2::b = 500;
}

#endif

/*
    多继承抛砖 测试加上virtual关键字后类的大小
    */
#if 0
class B
{
public:
    int b;
};
//加上virtual关键字后,则类中含有vptr指针,指向虚函数表的入口位置
class B1 :virtual public B
{
public:
    int b1;
};

class B2 : public B
{
public:
    int b2;
};

class C : public B1, public B2
{
public:
    int c;
};

void test()
{
    cout << sizeof(B) << endl;  //4
    cout << sizeof(B1) << endl; //12 //加上virtual以后 , C++编译器会在给变量偷偷增加属性
    cout << sizeof(B2) << endl; //8
}

#endif

/*
    类型兼容性原则遇上函数重写
    父类和子类有相同的成员函数,如果不写virtual,则子类初始化(或者赋值)父类
    时,只执行父类的同名函数,不满足需求。
    */
#if 0
class Parent
{
public:
    Parent(int a)
    {
        this->a = a;
        cout << "Parent a" << a << endl;
    }
    //在基类中加上virtual关键字后,子类可加可不加(默认是加上了),最好显示加上表明重写了
    virtual void print() //子类的和父类的函数名字一样
    {
        cout << "Parent 打印 a:" << a << endl;
    }
protected:
private:
    int a;
};

class Child : public Parent
{
public:
    Child(int b) : Parent(10)
    {
        this->b = b;
        cout << "Child b" << b << endl;
    }
    virtual void print() //virtual 父类写了virtual,子类可写 可不写 
    {
        cout << "Child 打印  b:" << b << endl;
    }
protected:
private:
    int b;
};

void howToPrint(Parent *base)
{
    base->print(); //一种调用语句 有多种表现形态...
}

void howToPrint2(Parent &base)
{
    base.print();
}
void test()
{
    Parent *base = NULL;
    Parent p1(20);
    Child c1(30);

    base = &p1;
    base->print();//执行父类的打印函数

    base = &c1;//子类对象初始化基类
    base->print();//执行谁的函数 ?  //面向对象新需求 加上了virtual,则有多态发生
    {
        Parent &base2 = p1;
        base2.print();

        Parent &base3 = c1; //base3是c1 的别名
        base3.print();
    }

    //函数调用
    howToPrint(&p1);
    howToPrint(&c1);//多态

    howToPrint2(p1);
    howToPrint2(c1);//多态
}
#endif

/*
    函数重写
    1、在子类中定义与父类中原型相同的函数
    2、函数重写只发生在父类与子类之间
    3、父类中被重写的函数依然会继承给子类
    4、默认情况下子类中重写的函数将隐藏父类中的函数
    5、通过作用域分辨符::可以访问到父类中被隐藏的函数

    多态案例、成立的三个条件、本质
    多态思想
    面向对象三大概念
    封装:突破c函数的概念,用类做函数参数的时候,可以使用对象的属性和对象的方法
    继承:A B代码复用
    多态:可以使用未来

    实现多态的三个条件
    c语言 间接赋值 是指针存在的最大意义,是c语言特有的现象
    (1 定义两个变量 2 建立关联 3 *p在被调用函数中去间接修改实参的值)

    实现多态的三个条件:
    1、要有继承
    2、要有虚函数重写
    3、用父类指针(父类引用)指向子类对象
    */
#if 0
class HeroFighter
{
public:
    virtual int power()//C++会对这个函数特殊处理
    {
        return 10;
    }
};
class EnemyFighter
{
public:
    int attack()
    {
        return 15;
    }
};

class AdvHeroFighter : public HeroFighter
{
public:
    virtual int power()
    {
        return 20;
    }
};

class AdvAdvHeroFighter : public HeroFighter
{
public:
    virtual int power()
    {
        return 30;
    }
};
/*
    多态威力
    PlayObj 给对象搭建舞台 看成一个框架!!!
    */
void playObj(HeroFighter *hf, EnemyFighter *ef)
{
    /*
    不写virtual关键字是静态联编 c++编译器根据HeroFighter 类型,去执行这个
    类型的power函数,在编译器编译阶段就已经决定了函数的调用,函数重载就是
    静态联编
    动态联编:迟绑定 在运行的时候,根据具体对象(具体的类型),执行不同对象
    的函数,表现成多态。
    */
    if (hf->power() > ef->attack())//hf->power()函数调用会有多态发生
    {
        cout << "主角win" << endl;
    }
    else
    {
        cout << "主角挂掉" << endl;
    }
}

void test()
{
    HeroFighter     hf;
    AdvHeroFighter  Advhf;
    EnemyFighter    ef;
    AdvAdvHeroFighter advadvhf;

    playObj(&hf, &ef);
    playObj(&Advhf, &ef);
    //playObj 这个框架能够把后来人写的代码给调用起来
    playObj(&advadvhf, &ef);
}
#endif

/*
    虚析构函数应用场景:通过父类指针释放所有的子类对象
    1、c++中不能声明虚构造函数。派生类对象撤销时,先调用派生类的析构,然后
    再调用基类的析构。
    delete无名对象时候,只执行基类的析构函数,而不执行派生类的析构函数原因
    是调用析构函数时,采用了静态联编方式,只调用了基类的析构函数。
    3、基类的析构函数加上virtual,虽然派生类和基类的析构函数名字不同,但由
    该基类所派生的所有派生类的析构函数也都自动成为虚函数。
    4、虚析构函数采用了动态联编,实现了运行时的多态。
    */
#if  0
class A
{
public:
    A()
    {
        p = new char[20];
        strcpy(p, "obja");
        cout << "A()" << endl;
    }
    //在父类中加上 virtual 关键字 虚析构函数,表现出多态
    virtual ~A()
    {
        delete[] p;
        cout << "~A()" << endl;
    }
private:
    char *p;
};

class B : public A
{
public:
    B()
    {
        p = new char[20];
        strcpy(p, "objb");
        cout << "B()" << endl;
    }
    ~B()
    {
        delete[] p;
        cout << "~B()" << endl;
    }
private:
    char *p;
};

class C : public B
{
public:
    C()
    {
        p = new char[20];
        strcpy(p, "objc");
        cout << "C()" << endl;
    }
    ~C()
    {
        delete[] p;
        cout << "~C()" << endl;
    }
private:
    char *p;
};

/*
   只执行了 父类的析构函数
   通过父类指针  把所有的子类对象的析构函数 都执行一遍
   通过父类指针 释放所有的子类资源
   */
void howtodelete(A *base)
{
    //基类中的析构不加virtual,则这句话不会表现成多态 这种属性
    delete base;
}

void howtodelete2(B *base)
{
    delete base;
}

void test()
{
    //使用new会调用对象的构造函数
    C *myC = new C;
    delete myC;//这种场景下直接通过子类对象释放所有资源,则不需要写virtual 
    printf("-----------------\n");

    C *myC0 = new C;
    //类型兼容性原则
    //通过父类释放所有的子类资源,需要在父类的析构函数前加virtual关键字,
    //否则会产生内存泄漏。
    howtodelete(myC0);

    printf("--------------\n");
    C *myC1 = new C;
    howtodelete2(myC1);
}
#endif

/*
    重载、重写、重定义
    函数重载:
    1、必须在同一个类中进行
    2、子类无法重载父类的函数,父类同名函数将被名称覆盖
    3、重载是在 编译期间 根据参数类型和个数决定函数调用

    函数重写:
    1、必须发生于父类与子类之间
    2、并且父类与子类中的函数必须有完全相同的函数原型
    3、使用virtual声明之后能够产生多态(不使用virtual,那叫重定义)
    4、多态是在 运行期间 根据具体对象的类型决定函数调用
    */
#if 0
class Parent
{
public:
    void abc()
    {
        printf("abc\n");
    }
    virtual void func()
    {
        cout << "func().." << endl;
    }
    virtual void func(int i)
    {
        cout << "func(int i).." << i <<endl;
    }
    virtual void func(int i, int j)//与子类的函数原型相同
    {
        cout << "func(int i, int j).." << i <<j<<endl;
    }
    virtual void func(int i, int j, int m, int n)
    {
        cout << "func(int i, int j, int m, int n).." << i << j << endl;
    }

};

class Child : public Parent
{

public:
    void abc()
    {
        printf("child abc");
    }
    /*
    void abc(int a)
    {
    printf("child abc");
    }
    */
    virtual void func(int i, int j)//虚函数的函数原型必须相同
    {
        cout << "func(int i, int j) do..." << i << " " << j << endl;
    }
    virtual void func(int i, int j, int k)
    {
        cout << "func(int i, int j) do.." << endl;
    }
protected:
private:
};

void test()
{
    Child c1;
    /*
     error C2661: “Child::func”: 没有重载函数接受 0 个参数
     func函数的名字,在子类中发生了名称覆盖;子类的函数的名字,占用了父类
     的函数的名字的位置。因为子类中已经有了func名字的重载形式。。。。,
     编译器开始在子类中找func函数。。。。但是没有0个参数的func函数
     */
    //c1.func();//不符合多态的条件
    //子类无法重载父类的函数,父类同名函数将被名称覆盖
    c1.Parent::func();

    //1 C++编译器 看到func名字 ,因子类中func名字已经存在了(名称覆盖).所以c++编译器不会去找父类的4个参数的func函数
    //2 c++编译器只会在子类中,查找func函数,找到了两个func,一个是2个参数的,一个是3个参数的.
    //3 C++编译器开始报错.....  error C2661: “Child::func”: 没有重载函数接受 4 个参数
    //4 若想调用父类的func,只能加上父类的域名..这样去调用..
    //c1.func(1, 2, 3, 4);
    c1.Parent::func(1, 2, 3, 4);
    //子类和父类中都有同名函数abc()且函数原型相同,是重定义
    c1.abc();
}
#endif

/*
    多态原理探究
    多态成立的三个条件:
    1、要有继承 2、虚函数重写 3、父类指针指向子类对象
    */
#if 0
class Parent
{
public:
    Parent(int a = 0)
    {
        this->a = a;
    }

    virtual void print()
    {
        cout << "我是爹1" << endl;//1 动手脚  写virtal关键字 会特殊处理 //虚函数表
    }
    virtual void print2()
    {
        cout << "我是爹2" << endl;//1 动手脚  写virtal关键字 会特殊处理 //虚函数表
    }
private:
    int a;
};
class Child : public Parent
{
public:
    Child(int a = 0, int b = 0) :Parent(a)
    {
        this->b = b;
    }

    virtual void print()
    {
        cout << "我是儿子" << endl;
    }
private:
    int b;
};

void HowToPlay(Parent *base)
{
    base->print();//有多态发生  //2 动手脚 
    //效果:传来子类对 执行子类的print函数 传来父类对执行父类的print函数 
    /*
       1、C++编译器根本不需要区分是子类对象 还是父类对象
       2、父类对象和子类对象分别有vptr指针 , ==>虚函数表===>函数的入口地址
       3、迟绑定 (运行时的时候,c++编译器才去判断)
       */

}

void test()
{
    Parent p1;//3 动手脚 提前布局 提前有了内存的布局 
    //用类定义对象的时候 C++编译器会在对象中添加一个vptr指针 
    Child c1; //子类里面也有一个vptr指针
    HowToPlay(&p1);
    HowToPlay(&c1);
}
#endif

/*
    证明vptr函数指针的存在
    */
#if 0
class Parent1
{
public:
    Parent1(int a = 0)
    {
        this->a = a;
    }

    void print()
    {
        cout << "我是爹" << endl;
    }
private:
    int a;
};

class Parent2
{
public:
    Parent2(int a = 0)
    {
        this->a = a;
    }

    virtual void print()
    {
        cout << "我是爹" << endl;
    }
private:
    int a;
};
void test()
{
    printf("sizeof(Parent):%d sizeof(Parent2):%d \n", sizeof(Parent1), sizeof(Parent2));
}
#endif

/*
    多态下对象内存布局
    */
#if 0
/*
    因为class A带有虚函数,所以A对象的内存布局就要增加一个
    虚函数表指针,它是一个指针,指向类A的虚函数表
    */
class A
{
public:
    A( int a1 = 0, int a2 = 0)
    {

    }
    virtual void A1() { cout << "A::A1" << endl; }
    virtual void A2() { cout << "A::A2" << endl; }
    virtual void A3() { cout << "A::A3" << endl; }
protected:
    int a1;
    int a2;
};
/*
    当父类有虚函数时,子类继承父类的虚函数表,而且虚函数的顺序是
    先父类的虚函数,再子类的虚函数;当父类的虚函数被子类重写时,
    则虚函数表中的父类虚函数指针要 替换 为子类的虚函数指针
    */
class B : public A
{
public:
    B(int a1 = 0, int a2 = 0, int b1 = 0):A(a1 = 1,a2 =2)
    {

    }
    virtual void B1() { cout << "B::B1" << endl; }
    virtual void A2() { cout << "B::A2" << endl; }//多态
    virtual void B2() { cout << "B::B2" << endl; }
protected:
    int b1;
};

typedef void(*pfun)();//定义函数指针类型
void test()
{
    B *bp= new B;
    pfun fun = NULL;//定义一个函数指针变量,用于循环迭代虚函数表
    printf("类B的内存模型:\n");
    //打印类B的内存模型
    for (int i = 0; i < 5; i++)
    {
        fun = (pfun)*((int *)*(int *)bp + i);//32平台
        printf("(int *)bp + %d = %x\n", i,(int *)bp + i);
        printf("*((int *)bp + %d) = %x\n", i,*( (int *)bp + i));

        printf("fun = %x\n",fun);
        fun();
    }
    cout << *((int*)*(int *)bp + 5) << endl;//输出虚函数表的结束符

}
/*
    (long*)bp,
    将对象的指针类型转换为(long*)类型,用于取出虚函数表的地址.
    *(long*)bp,
    *为取出指针指向的值.此式子即虚函数表的地址,也就是第一个虚函数的地址.
    (long*)*(long*)bp,
    将虚函数表的地址指针转换为(long*),用于后续迭代.
    *(long*)*(long*)bp,
    *求指针值,即为第一个虚函数的地址,最后转换为pfun指针.
    */
#endif

#if 0
struct A
{
    int a;
    int b;
    int c;
};
void test()
{
    printf("sizeof(void *) = %d\n", sizeof(void *));
    struct A a = {3,4,5};
    struct A *pa;
    pa = &a;
    printf("%p--- %d\n", (int *)pa, *(int *)pa);
    printf("%p--- %d\n", (int *)pa + 1,*((int *)pa+1));
    printf("%p--- %d\n", (int *)pa + 2, *((int *)pa + 2));

}
#endif

/*
    构造函数中能调用虚函数实现多态么?
    对象在创建的时,由编译器对VPTR指针进行初始化
    只有当对象的构造完全结束后VPTR的指向才最终确定
    父类对象的VPTR指向父类虚函数表
    子类对象的VPTR指向子类虚函数表

    */
#if 0
class Parent
{
public:
    Parent(int a = 0)
    {
        this->a = a;
        print();//父类中的构造调用不能实现多态
        //只有当对象的构造完全结束后VPTR的指向才最终确定
    }

    virtual void print()
    {
        cout << "我是爹" << endl;
    }

private:
    int a;
};

class Child : public Parent
{
public:
    Child(int a = 0, int b = 0) :Parent(a)
    {
        this->b = b;
        print();//子类中的构造调用多态不能实现多态
    }

    virtual void print()
    {
        cout << "我是儿子" << endl;
    }
private:
    int b;
};
void test()
{
    //定义一个子类对象 ,在这个过程中,在父类构造函数中调用虚函数print 能发生多态吗?
    Child  c1; 
    /*
        1、要初始化c1.vptr指针,初始化是分步
        2、当执行父类的构造函数时,c1.vptr指向父类的虚函数表
        当父类的构造函数运行完毕后,会把c1.vptr指向子类的虚函数表
        */
}

#endif

/*
    父类指针和子类指针的步长
    结论:
    多态是用父类指针指向子类对象 和 父类步长++ ,是两个不同的概念
    */
#if 0
class Parent
{
public:
    Parent(int a = 0)
    {
    }

    virtual void print()
    {
        cout << "我是爹" << endl;
    }

private:
    int a;
};
//成功 ,一次偶然的成功 ,必然的失败更可怕
class Child : public Parent
{
public:
    Child(int b = 0) :Parent(0)
    {
    }
    virtual void print()
    {
        cout << "我是儿子" << endl;
    }
private:
    //int b;//注释的情况下,父类与子类的 步长一致 不注释掉程序会崩溃
};

void test()
{
    Child c1;
    Parent *pP = NULL;
    Child  *pC = NULL;

    printf("sizeof(Parent) = %d\n", sizeof(Parent));
    printf("sizeof(Child) = %d\n", sizeof(Child));

    Child  array[] = { Child(1), Child(2), Child(3) };
    pP = array;
    pC = array;

    pP->print();//多态发生 1要有继承 2虚函数重写 3父类指针指向子类对象
    pC->print(); 

    pP++;
    pC++;
    pP->print();//多态发生
    pC->print(); 


    pP++;
    pC++;
    pP->print();//多态发生
    pC->print(); 



}
#endif

/*
    纯虚函数抽象类语法基础
    1、纯虚函数是一个在基类中说明的虚函数,在基类中没有定义,
    要求任何派生类都定义自己的版本
    2、纯虚函数为各派生类提供一个公共界面(接口的封装和设计、
    软件的功能模块划分)
    3、纯虚函数说明形式
    virtual 类型 函数名(参数表) = 0;

    纯虚函数的作用是在基类中为其派生类保留一个函数的名字,以便派生类根据
    需要对它进行重新定义。纯虚函数没有函数体,它最后的“=0”并不表示函数的
    返回值为0,只是起形式上的作用,告诉编译系统这是“这是纯虚函数”。
    纯虚函数不具备函数的功能,不能被调用。

    4、至少含有一个纯虚函数的基类称为抽象类
    5、抽象类不能建立对象,可以声明抽象类指针,可以指向它的派生类,进而实现多态性。
    6、抽象类不能作为参数类型和函数返回类型或显示转换的类型,可以声明抽象类的引用
    7、如果在抽象类中的派生类中没有重新说明纯虚函数,则该函数在派生类中仍然为纯虚函数
    ,而这个派生类仍然是一个抽象类

    */
#if 0
//面向抽象类编程(面向一套预先定义好的接口编程)
//解耦合  模块的划分

class Figure//抽象类
{
public:
    //阅读一个统一的界面(接口),让子类使用,让子类必须去实现
    virtual void getArea() = 0;//纯虚函数,基类中就不再给出函数的实现部分
protected:
private:
};

class Circle :public Figure
{
public:
    Circle(int a, int b)
    {
        this->a = a;
        this->b = b;
    }
    //注意实现这个纯虚函数接口的时候,从基类中拷贝最好,保证函数原型正确
    virtual void getArea()
    {
        cout << "圆形的面积:" << 3.14*a*a << endl;
    }
private:
    int a;
    int b;
};

class Tri :public Figure
{
public:
    Tri(int a, int b)
    {
        this->a = a;
        this->b = b;
    }
    virtual void getArea()
    {
        cout << "三角形的面积: " << a*b / 2 << endl;
    }
private:
    int a;
    int b;
};
class Square : public Figure
{
public:
    Square(int a, int b)
    {
        this->a = a;
        this->b = b;
    }
    virtual void getArea()
    {
        cout << "四边形的面积: " << a*b << endl;;
    }

private:
    int a;
    int b;
};

void objplay(Figure *base)
{
    base->getArea(); //会发生多态
}
void test()
{
    //Figure f; //抽象类不能被实例化,因为抽象类中包含一个没有定义功能的纯虚函数
    Figure *base = NULL; //可以声明为抽象类的指针
    Circle c1(10, 20);
    Tri t1(20, 30);
    Square s1(50, 60);

    c1.getArea();
    t1.getArea();
    s1.getArea();
    //面向抽象类编程(面向一套预先定义好的接口编程)
    printf("------------------\n");
    objplay(&c1);
    objplay(&t1);
    objplay(&s1);
}

#endif

/*
    多继承的二义性
    此种场景下加virtual关键字,则告诉c++编译器让老祖宗的构造函数只执行一次
    */
#if 0
class  B
{
public:
    int b;
protected:
private:
};

class  B1 : virtual public B
{
public:
    int b1;
protected:
private:
};

class  B2 : virtual public B
{
public:
    int b2;
protected:
private:
};

class  C : public B1, public B2
{
public:
    int c;
protected:
private:
};

void  test()
{
    C myc;
    myc.c = 10;
    myc.b = 100;//二义性  error C2385: 对“b”的访问不明确
    //加上virtual关键字,让父类只构造一次
}
#endif

/*
    抽象类在多继承中的应用
    绝大多数面向对象语言都不支持多继承
    绝大多数面向对象语言都支持接口的概念
    C++中没有接口的概念
    C++中可以使用纯虚函数实现接口
    接口类中只有函数原型定义,没有任何数据的定义

    */
#if 0
//两个接口类中函数名相同,也不会发生二义性
class Intetface1
{
public:
    virtual int add(int a, int b) = 0;//抽象类
    virtual void print() = 0;
};

class Intetface2
{
public:
    virtual int mult(int a, int b) = 0;//抽象类
    virtual void print() = 0;
};
class Parent
{
public:
    int getA()
    {
        a = 0;
        return a;
    }
protected:
private:
    int a;
};


class Child :public Parent, public Intetface1, public Intetface2
{
public:
    virtual int add(int a, int b)
    {
        cout << "Child: add()已经执行\n";
        return a + b;
    }
    virtual int mult(int a, int b)
    {
        cout << "Child: mult()已经执行\n";
        return a*b;
    }
    virtual void print()
    {
        cout << "Child: print()已经执行\n";
    }
protected:
private:
};
void test()
{
    Child c1;
    c1.print();//没有使用多态

    //声明指向抽象类的指针变量,指针指向它的派生类,实现多态
    Intetface1 *it1 = &c1;
    it1->add(1, 2);  //多态 1、要有继承 2、要有虚函数重写 3、用父类指针(父类引用)指向子类对象
    Intetface2 *it2 = &c1;
    it2->mult(1, 2); //多态
}
#endif

/*
    面向抽象编程类计算程序员工资
    */

#if 0
/*
编写一个C++程序, 计算程序员( programmer )工资
1 要求能计算出初级程序员( junior_programmer ) 中级程序员 ( mid_programmer )高级程序员( adv_programmer)的工资
2 要求利用抽象类统一界面,方便程序的扩展, 比如:新增, 计算 架构师 (architect ) 的工资
*/

class programmer
{
public:
    virtual void getSal() = 0;//纯虚函数 界面统一接口
};

class junior_programmer : public programmer
{
public:
    junior_programmer(char *name, char *job, int sal)
    {
        this->name = name;
        this->job = job;
        this->sal = sal;
    }

    virtual void getSal()
    {
        cout << name << " " << job << " : " << sal << endl;
    }
private:
    char * name;
    char *job;
    int sal;
};
class mid_programmer : public programmer
{
public:
    mid_programmer(char *name, char *job, int sal)
    {
        this->name = name;
        this->job = job;
        this->sal = sal;
    }
    virtual void getSal()
    {
        cout << name << " " << job << " : " << sal << endl;
    }
private:
    char *name;
    char *job;
    int sal;
};

//adv_programmer

class adv_programmer : public programmer
{
public:
    adv_programmer(char *name, char *job, int sal)
    {
        this->name = name;
        this->job = job;
        this->sal = sal;
    }
    virtual void getSal()
    {
        cout << name << " " << job << " : " << sal << endl;
    }
private:
    char *name;
    char *job;
    int sal;
};
class architect : public programmer
{
public:
    architect(char *name, char *job, int sal)
    {
        this->name = name;
        this->job = job;
        this->sal = sal;
    }
    virtual void getSal()
    {
        cout << name << " " << job << " : " << sal << endl;
    }
private:
    char *name;
    char *job;
    int sal;
};

void CalProgSal(programmer *base)
{
    base->getSal();//将发生多态
}

void test()
{
    junior_programmer jp("小张", "初级程序员", 4000);
    mid_programmer mp("小李","中级程序员", 7000);
    adv_programmer ap("小王", "高级程序员", 15000);

    //系统扩展
    architect ar("传智高水平学员", "架构师", 24000);
    CalProgSal(&jp);
    CalProgSal(&mp);
    CalProgSal(&ap);
    CalProgSal(&ar);
}
#endif

#if 0
//数组指针 语法 梳理
//定义一个数组类型   
//int a[10];//
//定义一个指针数组类型 
//定义一个指向 数组类型的指针 数组类的指针
void test()
{
    //a代表数组首元素的地址 &a代表整个数组的地址
    //a+1  4    &a +1  40
    int a[10];
    //定义一个数组类型   
    {
        typedef int(myTypeArray)[10];
        myTypeArray myArray;
        myArray[0] = 10;
        printf("%d \n", myArray[0]);
    }

    //定义一个数组指针类型 
    {
        typedef int(*PTypeArray)[10];//int *p
        PTypeArray myPArray;
        myPArray = &a;
        (*myPArray)[0] = 20;
        printf("a[0] = %d\n", a[0]);

    }
    //定义一个指向 数组类型的指针 数组类的指针
    {
        int(*MyPointer)[10];//变量 告诉c编译器给我分配内存
        MyPointer = &a;
        (*MyPointer)[0] = 40;
        printf("a[0] = %d\n", a[0]);

    }
}
#endif

//函数指针语法梳理
//1 如何定义一个函数类型
//2 如何定义一个函数指针类型
//3 如何定义一个 函数指针  (指向一个函数的入口地址)
#if 0
int add(int a, int b)
{
    printf("func add .....\n");
    return a + b;
}


void test()
{
    add(1, 2);//直接调用 函数名是函数的入口地址

    //1 如何定义一个函数类型
    {
        typedef int MyFuncType(int a, int b);
        //typedef int (MyFuncType)(int a, int b);
        MyFuncType *myPointerFunc = NULL;
        myPointerFunc = &add;
        myPointerFunc(3, 4);//间接调用

        myPointerFunc = add;//细节 c逐渐过程 兼容历史版本的原因
        myPointerFunc(3, 4);

    }
    //2 如何定义一个函数指针类型
    {
        typedef int(*MyPointerFuncType)(int a, int b);//int *a = NULL
        MyPointerFuncType myPointerFunc;//定义一个指针
        myPointerFunc = add;
        myPointerFunc(5, 6);
    }
    //3 如何定义一个 函数指针  (指向一个函数的入口地址)
    {
        int(*MyPointerFunc)(int a, int b);//定义了一个变量
        MyPointerFunc = add;
        MyPointerFunc(7, 8);

    }

}


#endif

/*
    函数指针做函数参数的思想剖析
    */
#if 0
int myadd(int a, int b)  //子任务的实现者
{
    printf("func add() do...\n");
    return a + b;
}
int myadd2(int a, int b)  //子任务的实现者
{
    printf("func add2() do...\n");
    return a + b;
}
int myadd3(int a, int b)  //子任务的实现者
{
    printf("func add3() do...\n");
    return a + b;
}
int myadd4(int a, int b)  //子任务的实现者
{
    printf("func add4() do...\n");
    return a + b;
}

//定义一个类型  相当于c++中的多态提前布局 函数指针类型就是规定好了协议(返回值、参数)
typedef int(*MyTypeFuncAdd)(int a, int b);

//函数指针做函数参数
int MainOp(MyTypeFuncAdd myFuncAdd)
{
    int c = myFuncAdd(5, 6);
    return c;
}

//定义一个函数类型变量
//int(*MyTypeFuncAdd)(int a, int b);
int MainOp2(int(*MyTypeFuncAdd)(int a, int b))
{
    int c = MyTypeFuncAdd(5, 6);
    return c;
}

//间接调用 

//任务的调用 和 任务的编写可以分开

void test()
{
    MyTypeFuncAdd myFuncAdd = NULL;
    myadd(1, 2);//直接调用

    myFuncAdd = myadd;
    myFuncAdd(3, 4);//间接调用

    MainOp(myadd);
    MainOp2(myadd);

    //在mainop框架 没有发生任何变化的情况下 ...
    MainOp(myadd2);
    MainOp(myadd3);
    MainOp(myadd4);
}
#endif

/*
    函数模板定义及2种调用方法
    法一: 显示类型 调用
    法二: 自动类型 推导
    */
#if 0
//函数的业务逻辑一样,只是函数的参数类型不同
void myswap01(int &a, int &b)
{
    int c = 0;
    c = a;
    a = b;
    b = c;
}
void myswap02(char &a, char &b)
{
    char c = 0;
    c = a;
    a = b;
    b = c;
}

/*
    让类型参数化,方便程序员编码 称为 泛型编程
    template 告诉c++编译器 我要开始泛型编程了 看到T,不要随便报错
    template语句与函数模板定义语句之间不能有别的语句
    */

template <typename T>
void myswap(T &a, T &b)
{
    T c = 0;
    c = a;
    a = b;
    b = c;
    cout << "hello ...我是模板函数, 欢迎call me!!" << endl;
}

void test()
{
    int x = 1;
    int y = 2;
    char a = 'g';
    char b = 'h';

    //法1 函数模板 显示类型调用
    myswap<int>(x, y);
    printf("x = %d y = %d\n", x, y);

    myswap<char>(a, b);
    printf("a = %c b = %c\n", a, b);

    //法2 自动类型推导 不常用
    myswap(x, y);
}
#endif

/*
    函数模板当函数参数
    函数模板的本质:类型参数化
    */
#if 0
//对字符数组和整形数组进行排序
template <typename T1, typename T2>
int mysort(T1 *array, T2 size)
{
    T2 i, j;
    T1 tmp;
    if (array == NULL)
    {
        return -1;
    }
    for (i = 0; i < size; i++)
    {
        for ( j = i+1; j < size; j++)
        {
            if (array[i] < array[j])
            {
                tmp = array[i];
                array[i] = array[j];
                array[j] = tmp;
            }
        }
    }
    return 0;
}

template<typename T1, typename T2>
void myprint(T1 * array, T2 size)
{
    T2 i = 0;
    for ( i = 0; i < size; i++)
    {
        cout << array[i] << " ";
    }
}
void test()
{
    //int类型
    int array[] = { 3,11,33,44,55,9 };
    int size = sizeof(array) / sizeof(int);
    mysort<int, int>(array, size);
    myprint<int, int>(array, size);
    printf("--------------------\n");
    //char 类型
    char buf[] = "awegfgddhh";
    int len = strlen(buf);
    mysort<char, int>(buf, len);
    myprint<char, int>(buf, len);
}
#endif

/*
    函数模板遇上函数重载
    */
#if 0
template <typename T>
void myswap(T &a, T &b)
{
    T c = 0;
    c = a;
    a = b;
    b = c;
    cout << "hello ...." << endl;
}

void myswap(int a, char c)
{
    cout << "a:" << a << "c:" << c << endl;
    cout << "我是普通函数" << endl;
}

void test()
{
    int a = 10;
    char c = 'z';

    myswap(a, c);
    //普通函数的调用:可以进行隐式类型转换
    myswap(c, a);
    //函数模板的调用(本质:类型参数化):将严格按照类型匹配,不会进行自动类型转换
    myswap(a, a);
}
#endif

/*
    函数模板不允许自动类型转化
    普通函数能够进行自动类型转换
    4条规则:
    1、函数模板可以像普通函数一样被重载
    2、c++编译器优先考虑普通函数
    3、如果函数模板可以产生一个更好的匹配,那么选择模板
    4、可以通过空模板实参列表的语法限定编译器只通过模板匹配
    */
#if 0
int Max(int a, int b)
{
    cout << "int Max(int a, int b)" << endl;
    return a > b ? a : b;
}

template<typename T>
int Max(T a, T b)
{
    cout << "int Max<>(T a, T b)" << endl;
    return a > b ? a : b;
}

template<typename T>
int Max(T a, T b, T c)
{
    cout << "int Max(T a, T b, T c)" << endl;
    return Max(Max(a, b), c);
}


void test()
{
    int a = 1;
    int b = 2;

    //当函数模板和普通函数都符合调用时,优先选择普通函数
    cout << Max(a, b) << endl;

    //若显示使用函数模板,则使用<>类型列表
    cout << Max<>(a, b) << endl;

    //如果函数模板产生更好的匹配,使用函数模板
    cout << Max(3.0, 4.0) << endl;

    //重载
    cout << Max(5.0, 6.0, 7.0) << endl;

    //调用普通函数 可以隐式 类型转换
    cout << Max('a', 100) << endl;
}
#endif

/*
    函数模板机制结论:
    编译器并不是把函数模板处理成能够任意类的函数
    编译器从函数模板通过具体类型产生不同的函数
    编译器会对模板进行2次编译:
    (1)在声明的地方对模板代码本身进行编译
    (2)在调用的地方对参数替换后的代码进行编译
    */
#if 0
// g++ -S 1.cpp  -o 1.s
template <typename T>
void myswap(T &a, T &b)
{
    T c = 0;
    c = a;
    a = b;
    b = c;
    cout << "hello ....我是模板函数 欢迎 calll 我" << endl;
}

void test()
{
    {
        int x = 10;
        int y = 20;
        myswap<int>(x, y); //1 函数模板 显示类型 调用
        printf("x:%d y:%d \n", x, y);
    }

    {
    char a = 'a';
    char b = 'b';
    myswap<char>(a, b); //1 函数模板 显示类型 调用
    printf("a:%c b:%c \n", a, b);
}
}

#endif

/*
    单个类 模板基本语法
    模板类 类型参数化
    类模板的定义、使用、类模板作为函数参数
    */
#if 0
template <typename T>
class A
{
public:
    A(T a = 0)
    {
        this->a = a;
    }

    void printA()
    {
        cout << "a: " << a << endl;
    }
protected:
    T a;
};

//类模板做函数参数
//参数 ,c++编译器 要求具体的类 , 所以要A<int>&a
void UseA(A<int> &a)
{
    a.printA();
}
/*
    类模板定义对象时,如下形式
    类模板名<实际类型名>对象名(实参表列)
    */
void test()
{
    //模板类(本身就是类型化的)==》具体的类==》定义具体的变量
    A <int>a1(11), a2(12), a3(30);//模板类是抽象的,需要进行类型具体
    a1.printA();
    UseA(a1);
    UseA(a2);
    UseA(a3);
}
#endif

/*
    从模板类派生普通类
    */
#if 0
template <typename T>
class A
{
public:
    A(T a)//构造函数写了,就必须用
    {
        this->a = a;
    }

    void printA()
    {
        cout << "a: " << a << endl;
    }
protected:
    T a;
};
/*
    从模板类派生普通类
    模板类派生时,需要具体化模板类,c++编译器需要知道 父类的数据类型具体是什么样子的
    ==》要知道父类所占的内存大小是多少 只有数据类型固定下来,才知道如何分配内存
    */
class B : public A<int>
{
public:
    B(int a = 10, int b = 20) : A<int>(a)
    {
        this->b = b;
    }
    void printB()
    {
        cout << "a: " << a << "b: " << b << endl;
    }
protected:
private:
    int b;
};

void test()
{
    B b1(1, 2);
    b1.printB();
}
#endif

/*
    从模板类派生模板类
    */
#if 0
template <typename T>
class A
{
public:
    A(T a)//构造函数写了,就必须用
    {
        this->a = a;
    }

    void printA()
    {
        cout << "a: " << a << endl;
    }
protected:
    T a;
};

template <typename T>
class C : public A<T>
{
public:
    C(T c, T a) : A<T>(a)
    {
        this->c = c;
    }
    void printC()
    {
        cout << "c:" << c << endl;
    }
protected:
    T c;
};
void test()
{
    C<int> c1(1, 2);
    c1.printC();
}
#endif

/*
    复数类所有函数都写在函数的内部
    */
#if 0
template <typename T>
class Complex
{
public:
    Complex(T a, T b)
    {
        this->a = a;
        this->b = b;
    }

    friend Complex MySub(Complex &c1, Complex &c2)
    {
        Complex tmp(c1.a - c2.a, c1.b - c2.b);
        return tmp;
    }

    friend ostream & operator<<(ostream &out, Complex &obj)
    {
        out << obj.a <<" + "<<obj.b<< "i"<< endl;
        return out;
    }

    Complex operator+(Complex &obj)
    {
        Complex tmp(this->a + obj.a, this->b + obj.b);
        return tmp;
    }
    void printCom()
    {
        cout << "a: " << a << " b: " << b << endl;
    }
private:
    T a;
    T b;
};

//运算符重载的正规写法
//重载<< >> 只能用友元函数,其他运算符重载都要写成成员函数,不要滥用友元函数
/*
 ostream & operator<<(ostream &out, Complex &obj)
 {
 out << obj.a << " + " << obj.b << "i" << endl;
 return out;
 }
 */
void test()
{
    //需要把模板类进行具体化以后 才能定义对象 c++编译器要分配内存
    Complex<int> c1(1, 2);
    c1.printCom();
    Complex<int> c2(3, 4);
    Complex<int> c3 = c1 + c2;
    c3.printCom();
    cout << c3 << endl;

    //滥用友元函数
    {
        Complex<int> c4 = MySub(c1, c2);
        cout << c4 << endl;

    }


}

#endif

/*
    复数类所有函数都写在函数的外部
    */
#if 0
//-------------------------------
//滥用友元函数的添加
template <typename T>
class Complex; //类的前置声明

template <typename T>
Complex<T> MySub(Complex<T> &c1, Complex<T> &c2);

//--------------------------------------------------------
template <typename T>
class Complex
{
    friend Complex<T> MySub<T>(Complex<T> &c1, Complex<T> &c2);

    friend ostream & operator<< <T> (ostream &out, Complex &obj);
    //friend ostream & operator<<(ostream &out, Complex &obj);

public:
    Complex(T a, T b);
    void printCom();
    Complex operator+(Complex &obj);

private:
    T a;
    T b;
};


/*
    成员函数从类的内部移到类的外部:
    1、在成员函数之前进行模板声明             template<typename T>
    2、在成员函数名前缀上 "类名<类型参数>::"    Complex<T>::

    在类模板外定义成员函数的一般形式如下
    template <typename 类型参数>
    函数类型 类名<类型参数>::成员函数名(形参表)

    注意友元函数是全局函数,不是成员函数

    */

template<typename T>
void Complex<T>::printCom()
{
    cout << "a: " << a << " b: " << b << endl;
}

template<typename T>
Complex<T>::Complex(T a, T b)
{
    this->a = a;
    this->b = b;
}

template<typename T>
Complex<T> Complex<T>::operator+(Complex<T> &obj)
{
    Complex tmp(this->a + obj.a, this->b + obj.b);
    return tmp;
}

/*
   注意友元函数是全局函数,是不属于这个类的
   出现以下Bug的原因:
   本质是:模板

   错误   LNK2019 无法解析的外部符号
   "class std::basic_ostream<char,struct std::char_traits<char> > &
   __cdecl operator<<
   (class std::basic_ostream<char,struct std::char_traits<char> > &,
   class Complex<int> &)"
   (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$Complex@H@@@Z),
   该符号在函数 "void __cdecl test(void)" (?test@@YAXXZ) 中被引用 生命周期管理
   G:\DAY\cproj\c++Proj\生命周期管理\生命周期管理\dm01类的构造和析构基础.obj 1
   */

template<typename T>
ostream & operator<<(ostream &out, Complex<T> &obj)
{
    out << obj.a << " + " << obj.b << "i" << endl;
    return out;
}

//滥用友元函数
template<typename T>
Complex<T> MySub(Complex<T> &c1, Complex<T> &c2)
{
    Complex<T> tmp(c1.a - c2.a, c1.b - c2.b);
    return tmp;
}
void test()
{
    //需要把模板类进行具体化以后 才能定义对象 c++编译器要分配内存
    Complex<int> c1(1, 2);
    c1.printCom();
    Complex<int> c2(3, 4);
    Complex<int> c3 = c1 + c2;
    c3.printCom();
    cout << c3 << endl;

    //滥用友元函数
    {
        Complex<int> c4 = MySub(c1, c2);
        cout << c4 << endl;

    }


}

#endif

/*goto 语句在多层嵌套结构中的应用*/
#if 0
void test()
{

    int i = 0;
    while (1)
    {
        i++;
        if (i == 5)
            goto END;
    }

END:
    printf(" END i = %d\n", i);

}
#endif

/*
    ****类模板中的static关键字****

    编译器并不是把函数模板处理成能够处理任意类的函数
    编译器从函数模板通过具体类型产生不同的函数,编译器会
    对函数模板进行两次编译
    在声明的地方对模板代码本身进行编译;
    在调用的地方对参数替换后的代码进行编译
    */
#if 0
template <typename T>
class AA
{
public:
    static T m_a;
protected:
private:
};
template <typename T>
T AA<T>::m_a = 0;

void test()
{
    AA<int> a1, a2, a3;
    a1.m_a++;
    a2.m_a++;
    a3.m_a++;
    cout << AA<int>::m_a << endl;

    AA<char> b1, b2, b3;
    b1.m_a = 'a';
    b2.m_a++;
    b3.m_a++;
    //m_a 应该是 每一种类型的类 使用自己的m_a
    cout << AA<char>::m_a << endl;
}

#endif

/*
    C++类型转换

    C风格的强制类型转换(Type Cast)很简单,不管什么类型的转换统统是:
    TYPE b = (TYPE)a
    C++风格的类型转换提供了4种类型转换操作符来应对不同场合的应用。
    static_cast     静态类型转换。如int转换成char
    reinterpreter_cast  重新解释类型
    dynamic_cast        命名上理解是动态类型转换。如子类和父类之间的多态类型转换。
    const_cast,     字面上理解就是去除变量的const属性。
    4种类型转换的格式:
    TYPE B = static_cast<TYPE> (a)

    */
#if 0
void test()
{
    double dpi = 3.1415926;
    //c类型转换
    int num1 = (int)dpi;
    //静态类型转换 编译时c++编译器会做类型检查
    int nmu2 = static_cast<int>(dpi);
    //c语言中 隐式类型转换的地方 均可使用 static_cast<>() 进行类型转换
    int num3 = dpi;

    //char *  ===> int *
    char *p1 = "hello world";
    int *p2 = NULL;
    //使用static_cast, 编译器编译时,会做类型检查 若有错误 提示错误
    //p2 = static_cast<int*>(p1);
    p2 = reinterpret_cast<int*>(p1);
    cout << "p1" << p1 << endl; //相当于 %s
    cout << "p2" << p2 << endl; //相当于 %d
    //总结:通过 reinterpret_cast<>() 和 static_cast<>()把C语言的强制类型转换 都覆盖了
}

#endif

/*
    dynamic_cast命名上理解是动态类型转换。如子类和父类之间的多态类型转换。
    */
#if 0
class Tree
{

};

//包含纯虚函数的类称之为抽象类
class Animal
{
public:
    virtual void cry() = 0;//纯虚函数 
};

class Dog :public Animal
{
public:
    virtual void cry()
    {
        cout << "汪汪" << endl;
    }
    void dohome()
    {
        cout << "看家" << endl;
    }
};

class Cat :public Animal
{
public:
    virtual void cry()
    {
        cout << "喵喵" << endl;
    }
    void dothing()
    {
        cout << "抓老鼠" << endl;
    }
};

void playobj(Animal *base)
{
    base->cry();//1有继承 2 虚函数重写 3父类指针指向子类对象 ==>多态
}

void playobj2(Animal *base)
{
    //能识别子类对象 运行时类型识别
    Dog *pDog = dynamic_cast<Dog *>(base);//转换不成功则指针为NULL
    if (pDog != NULL)
    {
        pDog->dohome();//让 能够做自己的工作
    }

    Cat *pCat = dynamic_cast<Cat *>(base);//父类对象 ==>子类对象  向下转型 把老子转化为小子
    if (pCat != NULL)
    {
        pCat->dothing();//让 能够做自己的工作
    }
}

void test()
{
    Dog d1;
    Cat c1;
    //Animal pBase; 不能定义抽象类对象 只能定义抽象类指针
    Animal *pBase = NULL;

    //子类对象复制给父类
    pBase = &d1;//第一种写法

    //c++编译器在编译的时候会进行类型检查
    pBase = static_cast<Animal *>(&d1);//第二种写法

    //强制类型转换
    pBase = reinterpret_cast<Animal *>(&d1);//第三种写法

    {
        Tree t1;
        //pBase = static_cast<Animal *>(&t1);
        //强制类型转换 Tree 类型强制转化为 Animal
        pBase = reinterpret_cast<Animal *>(&t1);

    }
    playobj(&d1);
    playobj(&c1);

    playobj2(&d1);
    playobj2(&c1);
}
#endif

/*
    const_cast去除变量的const属性。
    */
#if 0
void printBuf(const char * p)
{
    //p[0] = 'k';
    char *p1 = NULL;
    //程序员 要清楚的知道 变量 : 转换之前是什么类型, 转换之后是什么类型
    //const char * ===> char * //把只读属性 去掉
    p1 = const_cast<char *>(p);
    p1[0] = 'K';//通过p1 去修改了内存空间
    cout << p << endl;


}
void test()
{
    char buf[] = "123456";//分配了内存

    char *myp = "hhhhhh";//在静态存储区
    printBuf(buf);
    //程序员 要确保 p所指向的内存空间 确实能修改 ;如果不能修改会带来灾难性后果
    printBuf(myp);
}

#endif

/*
    异常的基本语法
    1 基本语法
    2 发生异常之后,是跨函数 :
    3 接受异常以后 可以不处理 再抛出异常
    4 catch异常的时 按照类型进行catch
    5 异常捕捉严格按照类型匹配
    */
#if 0

void devide(int x, int y)
{
    if (y == 0)
    {
        throw x;//抛出int类型异常
    }
    cout << "devide 结果" << x / y << endl;
}

void myDevide(int x, int y)
{
    try
    {
        devide(x, y);
    }
    catch (...)
    {
        cout << "我接受了 divide的异常 但是我没有处理 我向上抛出" << endl;
        throw;
    }
}
void test()
{
    try
    {       
        devide(10, 2);
        //devide(100, 0);
        myDevide(344, 0);
    }
    catch (int e)
    {
        cout << e << "被零除" << endl;
    }
    catch (...)//... 代表其他未知类型
    {
        cout << "其他未知错误" << endl;
    }
}

//异常捕捉严格按照类型匹配
void test01()
{
    try
    {
        throw 'Z';
    }
    catch (int e)
    {
        cout << "捕获int类型异常" << endl;
    }
    catch (...)
    {
        cout << " 未知 类型异常" << endl;
    }
}
#endif

/*
    异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上的构造的所有对象,
    都会被自动析构。析构的顺序与构造的顺序相反。这一过程称为栈的解旋

    为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类型
    void func() throw (A, B, C, D);
    这个函数func()能够且只能抛出类型A B C D及其子类型的异常
    */
#if 0
class Test3
{
public:
    Test3(int a, int b)
    {
        this->a = a;
        this->b = b;
        cout << "构造函数do" << endl;
    }

    ~Test3()
    {
        cout << "析构函数do" << endl;
    }

private:
    int a;
    int b;
};

//不写,可以抛掷任何类型的异常
void myDivide01()
{
    Test3 t1(1, 2), t2(3, 4);
    cout << "myDevide 要发生异常" << endl;
    throw 1;//相当于return
}


//抛出的类型只能是所列出的类型
void myDivide02() throw(int, char, char *)
{
    Test3 t1(1, 2), t2(3, 4);
    cout << "myDevide 要发生异常" << endl;
    throw t1;//测试
    throw 1;//相当于return
}

//不抛掷任何类型异常
void myDivide03() throw()
{
    Test3 t1(1, 2), t2(3, 4);
    cout << "myDevide 要发生异常" << endl;
    throw 1;//相当于return
}

void test()
{
    try
    {
        //myDivide01();
        myDivide02();
        //myDivide03();
    }
    catch (int a)
    {
        cout << "int 类型异常"<<endl;
    }
    catch (...)
    {
        cout << "未知异常" << endl;
    }
}
#endif

/*
    传统错误处理
    结论:
    C++编译器通过throw 来产生对象,C++编译器再执行对应的catch分支,
    相当于一个函数调用,把实参传递给形参。
    */
#if 0
//throw int 类型异常
void my_strcpy1(char *to, char * from)
{
    if (from == NULL)
    {
        throw 1;//抛出整型对象
    }
    if (to == NULL)
    {
        throw 2;
    }

    if (*from == 'a')
    {
        throw 3;
    }

    while (*from != '\0')
    {
        *to = *from;
        to++;
        from++;
    }
    *to = '\0';
}

//throw char* 类型异常
void my_strcpy2(char *to, char * from)
{
    if (from == NULL)
    {
        throw "源buf出错";//抛出字符串,存储在静态存储区
    }
    if (to == NULL)
    {
        throw "目的buf出错";
    }

    if (*from == 'a')
    {
        throw "copy过程出错"; //copy时出错
    }

    while (*from != '\0')
    {
        *to = *from;
        to++;
        from++;
    }
    *to = '\0';
}

class BadSrcType {};

class BadDestType {};

class BadProcessType
{
public:
    BadProcessType()
    {
        cout << "BadProcessType 构造函数do \n";
    }


    BadProcessType(const BadProcessType &obj)
    {
        cout << "BadProcessType copy构造函数do \n";
    }

    ~BadProcessType()
    {
        cout << "BadProcessType 析构函数do \n";
    }

};

//throw 类对象 类型异常
void my_strcpy3(char *to, char * from)
{
    if (from == NULL)
    {
        throw BadSrcType();//注意 调用无参不加括号,这里加了括号,记住就可以了
    }
    if (to == NULL)
    {
        throw BadDestType();//抛出一个子类对象
    }

    if (*from == 'a')
    {
        printf("开始 BadProcessType类型异常 \n");
        throw BadProcessType(); //会产生一个对象(不管是匿名还有有名)
    }

    if (*from == 'b')
    {
        printf("开始 BadProcessType类型异常 \n");
        throw &(BadProcessType()); //会产生一个对象(指针)(不管是匿名还有有名)
    }

    if (*from == 'c')
    {
        printf("开始 BadProcessType类型异常 \n");
        throw new BadProcessType; //抛出一个对象指针,分配了内存
    }
    while (*from != '\0')
    {
        *to = *from;
        to++;
        from++;
    }
    *to = '\0';
}

void test()
{
    int ret = 0;
    char buf1[] = "abcde";
    //char buf1[] = "bcde";
    //char buf1[] = "cde";

    char buf2[1024] = { 0 };

    try
    {
        //my_strcpy1(NULL, buf1);
        //my_strcpy2(NULL, buf1);
        my_strcpy3(buf2, buf1);
    }
    catch (int e)//e 可以写 也可以不写
    {
        cout << e << "  int 类型异常" << endl;
    }

    catch (char *e)//e 可以写 也可以不写 指针是谁分配的内存
    {
        cout << e << " char 类型异常" << endl;
    }

    //---
    catch (BadSrcType e)
    {
        cout << " BadSrcType 类型异常" << endl;
    }

    catch (BadDestType e)
    {
        cout << " BadDestType 类型异常" << endl;
    }

#if 0
    //结论1 如果 接受异常的时候,使用一个异常变量,则copy构造异常变量
    catch (BadProcessType e) //把匿名对象copy给e
    {
        cout << " BadProcessType 类型异常" << endl;
    }
#endif

#if 1
    //选择引用比较合适
    //结论2 使用引用的话 会使用throw时候的那个对象
    //catch 一个元素和引用不能同时存在
    catch (BadProcessType &e) //把匿名对象copy给e
    {
        cout << " BadProcessType 类型异常" << endl;
    }

#endif
    //结论3: 指针可以和 引用/元素写在一块 但是引用/元素不能写在一块
    catch (BadProcessType *e)
    {
        cout << " BadProcessType 类型异常" << endl;
        delete e;//使用new时候,需要delete
    }

    //结论4: 类对象时, 使用引用比较合适 
    // --
    catch (...)
    {
        cout << "未知 类型异常" << endl;
    }
}
#endif

/*
    异常的层次结构 (继承在异常中的应用)
    */
#if 1
class MyArray
{
public:
    MyArray(int len);
    ~MyArray();

public:
    int& operator[](int index);
    int getLen();
#if 0
    class eNegative{};
    class eZero{};
    class eTooBig{};
    class eTooSmall{};
    class eSize{};
#else
    //内部类
    //基类
    class eSize
    {
    public:
        eSize(int size)
        {
            m_size = size;
        }
        //虚函数
        virtual void printErr()
        {
            cout << "size:" << m_size << " ";
        }
    protected:
        int m_size;
    };

    //让类发生继承
    class eNegative :public eSize
    {
    public:
        eNegative(int size) : eSize(size)//父类构造函数写了就必须用
        {
            ;
        }
        virtual void printErr()
        {
            cout << "eNegative 类型 size:" << m_size << " ";
        }
    };

    class eZero :public eSize
    {
    public:
        eZero(int size) :eSize(size)
        {
            ;
        }
        virtual void printErr()
        {
            cout << "eZero 类型 size:" << m_size << " ";
        }
    };

    class eTooBig :public eSize
    {
    public:
        eTooBig(int size) :eSize(size)
        {
            ;
        }
        virtual void printErr()
        {
            cout << "eTooBig 类型 size:" << m_size << " ";
        }
    };

    class eTooSmall :public eSize
    {
    public:
        eTooSmall(int size) :eSize(size)
        {
            ;
        }
        virtual void printErr()
        {
            cout << "eTooSmall 类型 size:" << m_size << " ";
        }
    };

#endif

private:
    int *m_space;
    int m_len;
};

MyArray::MyArray(int len)
{
    if (len < 0)
    {
        throw eNegative(len);//抛出的子类对象
    }
    else if (len == 0)
    {
        throw eZero(len);
    }
    else if (len > 1000)
    {
        throw eTooBig(len);
    }
    else if (len < 3)
    {
        throw eTooSmall(len);
    }

    m_len = len;
    m_space = new int[len];
}

MyArray::~MyArray()
{
    if (m_space != NULL)
    {
        delete[]m_space;
        m_space = NULL;
        m_len = 0;
    }
}

int& MyArray::operator[](int index)
{
    return m_space[index];
}

int MyArray::getLen()
{
    return m_len;
}

//不推荐
void test01()
{
    try
    {
        MyArray a(-5);
        for (int i = 0; i < a.getLen(); i++)
        {
            a[i] = i + 1;
            printf("%d", a[i]);
        }
    }
    catch (MyArray::eNegative e)
    {
        cout << "eNegative 类型异常" << endl;
    }
    catch (MyArray::eZero e)
    {
        cout << "eZero 类型异常" << endl;
    }
    catch (MyArray::eTooBig e)
    {
        cout << "eTooBig 类型异常" << endl;
    }
    catch (MyArray::eTooSmall e)
    {
        cout << "eTooSmall 类型异常" << endl;
    }
    catch (...)
    {

    }
}

void test()
{
    try
    {
        MyArray a(-5);
        for (int i = 0; i < a.getLen(); i++)
        {
            a[i] = i + 1;
            printf("%d", a[i]);
        }
    }
    catch (MyArray::eSize &e) //原则:throw的子类 catch父类
    {
        e.printErr();//发生多态
    }

    catch (...)
    {
    }
}
#endif

#if 0
class Teacher
{
public:
    Teacher(int age)
    {
        if (age > 100)
        {
            string s = "年龄太大";
            //out_of_range 表示一个参数不在允许的范围之内
            throw out_of_range(s);
        }
    }
private:
    int age;
};

void test()
{
    try
    {
        Teacher t1(102);
    }
    catch (out_of_range e)
    {
        cout << e.what() << endl;
    }
    //exception e;
}
#endif

/*
    标准异常基类exception,它的成员函数what()
    函数原型:
    virtual const char *  what() const;//最右边的const修饰的是this指针
    */
#if 0

class MyException :public exception
{
public:
    MyException(const char *p)
    {
        this->m_p = p;
    }
    virtual const char * what()const
    {
        cout << "MyException: 类型" << m_p << endl;
        return m_p;
    }
protected:
private:
    const char *m_p;
};

void testMyExcept()
{
    throw MyException("函数异常");//抛出子类对象
}

void test()
{
    try
    {
        testMyExcept();
    }

    catch (MyException &e)//使用引用
    {
        e.what();//没有发生多态
    }
    //catch (exception &e)//使用引用
    //{
    //  e.what();//发生了多态
    //}
}
#endif

/*
    I/O流的概念和流类库
    标准I/O:
    对系统指定的标准设备的输入和输出。从键盘输入数据,输出到显示器屏幕

    文件I/O:
    以外存磁盘文件为对象进行输入和输出,即从磁盘文件输入数据,数据输出到磁盘文件。

    串I/O:
    对内存中指定的空间进行输入和输出。通常指定一个字符数组作为存储空间(实际上
    可以利用该空间存储任何信息)

    C++的I/O操作是可扩展的,输出标准数据也可以输出自定义数据

    iostream:输入输出流


    >>a表示将数据放入a对象中。
    <<a表示将a对象中存储的数据拿出

    标准输入流对象cin
    cin.get() //一次只能读取一个字符
    cin.get(一个参数) //读一个字符
    cin.get(三个参数) //可以读字符串
    cin.getline()可以接受空格
    cin.ignore() 忽略缓冲区的个数
    cin.peek() 偷窥,打开门若缓冲区有数据就取缓冲区的第一个字符,没有就拉倒
    cin.putback() 将读取缓冲区中的第一个字符再还给缓冲区
    */
#if 0

void test01()
{
    char mybuf[1024];//注意分配内存
    int myInt;
    long myLong;

    cin >> myInt;
    cin >> myLong;
    cin >> mybuf;//遇见空格停止接受数据

    cout << "myInt = " << mybuf << endl << "myLong = " << myLong << endl << "mybuf = " << mybuf << endl;
}

//cin.get() //一次只能读取一个字符
void test02()
{
    //EOF  用ctrl+Z 再按下enter
    char ch;
    while ((ch = cin.get()) != EOF)
    {
        cout << ch << endl;
    }
}
//cin.get(一个参数) //读一个字符
void test03()
{
    char c1, c2, c3;

    cout << "cin.get()如果缓冲区没有数据,则程序阻塞" << endl;
    cin.get(c1);
    cin.get(c2);
    cin.get(c3);
    cout << "---------" << endl;
    cout << c1 << c2 << c3 << "因为缓冲区有数据,程序不会阻塞\n";
    cin.get(c1).get(c2).get(c3);//支持链式编程
    cout << "--" << c1 << c2 << c3 << endl;
}

//cin.getline()可以接受空格
void test04()
{
    char buf1[256];
    char buf2[256];

    cout << "请输入一个字符串,含有多个空格aa bb cc dd \n";
    cin >> buf1;//遇到空格就会结束输入

    cin.getline(buf2, 256);
    cout << "buf1:" << buf1 << "buf2" << buf2 << endl;

}

//cin.ignore() 忽略缓冲区的个数
void test05()
{
    char buf1[256];
    char buf2[256];

    cout << "请输入一个字符串,含有多个空格aa  bbccdd \n";
    cin >> buf1;//遇到空格就会结束输入
    cin.ignore(2);//忽略2 个字符 ,若忽略的字符个数大于缓冲区的字符个数,会阻塞的
    //cin.ignore(20); 
    int myint = cin.peek();//偷窥下缓冲区是否有数据,若有,则读取缓冲区的第一个数据
    cout << "myint" << myint << endl;//一般用于网络上
    cin.getline(buf2, 256);
    cout << "buf1:" << buf1 << "\nbuf2:" << buf2 << endl;
}

//案例:输入的整数和字符串分开处理
void test06()
{
    cout << "Please, enter a number or a word: ";
    char c = std::cin.get();//先从缓冲区读取了一个字符

    if ((c >= '0') && (c <= '9')) //输入的整数和字符串 分开处理
    {
        int n; //整数不可能 中间有空格 使用cin >>n
        cin.putback(c);
        cin >> n;
        cout << "You entered a number: " << n << '\n';
    }
    else
    {
        string str;
        cin.putback(c);//将字符在吐回给缓冲区
        //cin.getline(str);
        getline(cin, str); // //字符串 中间可能有空格 使用 cin.getline();
        cout << "You entered a word: " << str << '\n';
    }
}
void test()
{
    //test01();
    //test02();
    //test03();
    //test04();
    //test05();
    test06();
}
#endif

/*

    c++格式化输出
    标准输出流对象cout
    cout.flush()    刷新缓冲区
    cout.put()
    cout.write()//进行二进制流的输入和输出
    cout.width()
    cout.fill()
    cout.setf(标记)

    manipulator(操作符、控制符)
    flush
    endl
    oct
    dec
    hex
    setbase
    setw
    setfill
    setprecision
    …
    */
#if 0
#include <iomanip>
void test01()
{
    cout << "hello" << endl;
    cout.put('h').put('e').put('l') << endl;

    char *p = "12345 itcast";
    cout.write(p, strlen(p)) << endl;
    cout.write(p, strlen(p) - 4) << endl;
    cout.write(p, strlen(p) + 4) << endl;
}

void test02()
{
    //使用流对象的有关类成员函数
    cout << "<start>";
    cout.width(30);
    cout.fill('*');
    cout.setf(ios::showbase);
    //cout.setf(ios::internal);//注释与不注释的效果
    cout << hex << 123 << "<end>" << endl;

    //使用控制符
    cout << "<start>"
        << setw(30)
        << setfill('*')
        << setiosflags(ios::showbase)//基数
        << setiosflags(ios::internal)
        << hex
        << 123
        << "<end>"
        << endl;
}

/*
    使用控制符(又称操纵符)输出 需要添加iomanip头文件
    dec hex oct
    ws:用于在输入的时候跳过空白符
    endl:输出一个换行符并刷新输出流
    ends:插入一个空字符null,通常用来结束一个字符串
    flush:刷新一个输出流
    setbase(n):设置整数的基数为n,n的取值0,8,10,16 ,默认为0,十进制
    setfill(c):设置c为填充空格,默认为空格
    setw(n):设置域宽为n
    setprecision(n):设置实数的精度为n 位
    在以一般十进制小数形式输出时,n代表有效数字
    在以fixed(固定小数位数)形式和scientific(指数)形式输出时,n为小数位数

    setiosflags(f):设置参数f指定的状态标识
    resetiosflags(f):终止参数f指定的状态标识

    setiosflags(ios::left)  数据按域宽 左对齐 输出
    setiosflags(ios::right) 数据按域宽 右对齐 输出
    setiosflags(ios::fixed) 固定的小数位数显示
    setiosflags(ios::scientific)    设置浮点数以科学计数法(即指数形式)显示
    setiosflags(ios::showpos)   在正数前添加一个‘+’号
    setiosflags(ios::uppercase) 在以科学计数法E和十六进制输出字母时用大写表示
    setiosflags(ios::skipws)    忽略前导空格
    setiosflags(ios::internal)数值的符号位在域宽内左对齐,数值右对齐,中间由填充字符填充
    */
void test03()
{
    int a;
    cout << "input a:";
    cin >> a;
    //以十进制形式输出整数
    cout << "dec:" << dec << a << endl;
    //以十六进制形式输出整数
    cout << "hex:" << hex << a << endl;
    //以八进制形式输出整数a
    cout << "oct:" << setbase(8) << a << endl;

    char *pt = "china";
    //指定域宽为10,输出字符串
    cout << setw(10) << pt << endl;
    //setw(n)指定域宽,输出字符串,空白处以'*'填充setfill(c)
    cout << setfill('*') << setw(10) << pt << endl;

    double pi = 22.0 / 7.0;//计算pi值

    cout << "pi = " << setiosflags(ios::fixed) << pi << endl;

    //按指数形式输出8位小数
    cout << setiosflags(ios::scientific) << setprecision(8);

    cout << "pi = " << pi << endl;//输出pi值
    //按指数形式输出4位小数
    cout << "pi = " << setprecision(4) << pi << endl;
    //改为小数形式输出 注意resetiosflags(f):终止参数f指定的状态标识
    cout << "pi = " << setiosflags(ios::fixed) << pi << endl;
}

void test04()
{
    double a = 123.456, b = 3.14159, c = -3214.67;
    cout << setiosflags(ios::fixed) << setiosflags(ios::right) << setprecision(2);
    cout << setw(10) << a << endl;
    cout << setw(10) << b << endl;
    cout << setw(10) << c << endl;

}

void test05()
{
    int a = 21;

    cout.setf(ios::showbase);       //显示基数符号(0x或)
    cout << "dec:" << a << endl;    //默认以十进制形式输出a
    cout.unsetf(ios::dec);          //终止十进制的格式设置

    cout.setf(ios::hex);            //设置以十六进制输出的状态
    cout << "hex:" << a << endl;    //以十六进制形式输出a
    cout.unsetf(ios::hex);          //终止十六进制的格式设置

    cout.setf(ios::oct);            //设置以八进制输出的状态
    cout << "oct:" << a << endl;    //以八进制形式输出a
    cout.unsetf(ios::oct);

    char *pt = "China";         //pt指向字符串"China"
    cout.width(10);         //指定域宽为
    cout << pt << endl;     //输出字符串

    cout.width(10);         //指定域宽为
    cout.fill('*');         //指定空白处以'*'填充
    cout << pt << endl;     //输出字符串

    double pi = 22.0 / 7.0; //输出pi值
    cout.setf(ios::scientific); //指定用科学记数法输出
    cout << "pi=";          //输出"pi="
    cout.width(14);         //指定域宽为
    cout << pi << endl;     //输出pi值

    cout.unsetf(ios::scientific);   //终止科学记数法状态

    cout.setf(ios::fixed);          //指定用定点形式输出
    cout.width(12);                 //指定域宽为
    cout.setf(ios::showpos);    //正数输出“+”号
    cout.setf(ios::internal);   //数符出现在左侧
    cout.precision(6);          //保留位小数
    cout << pi << endl;         //输出pi,注意数符“+”的位置

}
void test()
{
    //test01();
    //test02();
    //test03();
    //test04();
    test05();
}
#endif

/*
    文件I/O
    1、输入输出是以系统指定的标准设备(输入设备为键盘,输出设备为显示器)为对象的。
    在实际应用中,常以磁盘文件作为对象。即从磁盘文件读取数据,将数据输出到磁盘文件。
    2、输入输出类主要在fstream.h这个头文件,定义了三个类,控制对文件的各种输入输出操作,
    他们分别是ifstream、ofstream、fstream,其中fstream类是由iostream类派生而来,
    3、 ifstream类,它是从istream类派生的,用来支持从磁盘文件的输入。
    ofstream类,它是从ostream类派生的,用来支持向磁盘文件的输出。
    fstream类,它是从iostream类派生的,用来支持对磁盘文件的输入输出
    4、
    输出流用来写文件  out--->输出文件
    输入流用来读文件  in <---输入文件
    */
#if 0
#include<fstream>
void test01()
{
    char *fname = "d:/11.txt";
    ofstream fout(fname, ios::app);//建一个 输出流对象 和文件关联;
    if (!fout)
    {
        cout << "打开文件失败" << endl;
        return;
    }
    fout << "hello...111" << endl;
    fout << "hello...222" << endl;
    fout << "hello...333" << endl;

    fout.close();

    //读文件
    ifstream fin(fname);//建立一个输入流对象 和文件关联
    char ch;
    while (fin.get(ch))
    {
        cout << ch;
    }
    fin.close();

}

class Teacher
{
public:
    Teacher()
    {
        age = 0;
        strcpy(name, "");
    }
    Teacher(int _age, char *_name)
    {
        age = _age;
        strcpy(name, _name);
    }
    void prinT()
    {
        cout << "age:" << age << "name:" << name << endl;
    }

private:
    int age;
    char name[32];

};

/*
    二进制文件
    对二进制文件的读写主要用istream类的成员函数read和write来实现。
    这两个成员函数的原型为
    istream& read(char *buffer,int len);
    ostream& write(const char * buffer,int len);
    */
void test02()
{
    char *fname = "d:/2a.dat";
    //读文件
    ofstream fout(fname, ios::binary);//建一个 输出流对象 和文件关联;  
    if (!fout)
    {
        cout << "打开文件失败" << endl;
        return;
    }
    Teacher t1(31, "t31");
    Teacher t2(32, "t32");
    fout.write((char*)&t1, sizeof(t1));
    fout.write((char*)&t2, sizeof(t2));
    fout.close();

    //写文件
    ifstream fin(fname);//建立一个输入流对象 和文件关联

    Teacher tmp;
    fin.read((char*)&tmp, sizeof(Teacher));
    tmp.prinT();
    fin.read((char*)&tmp, sizeof(Teacher));
    tmp.prinT();
    fin.close();
}

void test03()
{
    char fileName[80];
    char buffer[255];
    cout << "请输入一个文件名:";
    cin >> fileName;//从屏幕读取数据到fileName

    //写文件
    ofstream fout(fileName, ios::app);
    fout << "1111111111111\n";
    fout << "2222222222222\n";

    cin.ignore(1, '\n');
    cin.getline(buffer, 255);//将屏幕上读取的数据写入 buffer
    fout << buffer << "\n";//将buffer中的数据写入文件
    fout.close();

    //读文件
    ifstream fin(fileName);

    cout << "Here's the content of the file:\n";
    char ch;
    while (fin.get(ch))
        cout << ch;
    cout << "\n*** End of file contents.***\n";
    fin.close();
}

/*
    在定义ifstream和ofstream类对象的时候,我们也可以不指定文件。
    以后可以通过成员函数open()显式的把一个文件连接到一个类对象上

    利用ofstream类对象,将内容写入到文件中
    */
void test04()
{
    ofstream myfile;//定义一个输出类对象
    myfile.open("d:/1.txt", ios::app | ios::out);
    if (!myfile)
    {
        cout << "打开文件失败" << endl;
    }
    myfile << "传智播客" << endl << "网址: " << "www.itcast.cn";
    myfile.close();

}

/*
    利用ifstream类对象,将文件中的数据读取出来,然后再输出到标准设备
    */
void test05()
{
    ifstream myfile;//定义一个输入类对象
    myfile.open("d:/2.txt", ios::app|ios::in);
    if (!myfile)
    {
        cout << "文件读错误";
        system("pause");
        exit(1);
    }
    char ch;
    string content;
    while (myfile.get(ch))//get()函数读到文件末尾返回假值
    {
        content += ch;
        cout.put(ch);//cout << ch;
    }

    myfile.close();
    cout << content;
}
/*
    利用fstream类对象可以同对文件进行读写操作
    */
void test06()
{
    fstream myfile;
    //写文件
    myfile.open("d:/2.txt", ios::out|ios::app);
    if (!myfile)
    {
        cout << "文件写错误,文件属性可能为只读" << endl;
        system("pause");
        exit(1);
    }
    myfile << "船只" << "sdfg" << endl;
    myfile.close();
    //读文件
    myfile.open("d:/2.txt", ios::in|ios::app);
    if (!myfile)
    {
        cout << "文件读错误,文件可能丢失" << endl;
        system("pause");
        exit(1);
    }
    char ch;
    while (myfile.get(ch))
    {
        cout.put(ch);
    }
    myfile.close();
}
void test()
{
    //test01();
    //test02();
    //test03();
    //test04();
    //test05();
    test06();

}
#endif

#if 0
#include<fstream>
/*
编程实现以下数据输入/输出:
(1)以左对齐方式输出整数,域宽为12。
(2)以八进制、十进制、十六进制输入/输出整数。
(3)实现浮点数的指数格式和定点格式的输入/输出,并指定精度。
(4)把字符串读入字符型数组变量中,从键盘输入,要求输入串的空格也全部读入,以回车符结束。
(5)将以上要求用流成员函数和操作符各做一遍。
*/
void test01()
{
    long a = 234;
    double b = 2345.67890;
    char c[100];

    //流成员函数实现
    cout.fill('*');
    cout.flags(ios_base::left);
    cout.width(12);
    cout << a << endl;
    //---------------------
    cout.fill('*');
    cout.flags(ios::right);
    cout.width(12);
    cout << a << endl;

    cout.flags(ios::hex);
    cout << 234 << '\t';

    cout.flags(ios::dec);
    cout << 234 << '\t';

    cout.flags(ios::oct);
    cout << 234 << endl;
    //------
    cout.flags(ios::scientific);
    cout << b << '\t';

    cout.flags(ios::fixed);
    cout << b << endl;

    cin.get(c, 99);
    cout << c << endl;

    //操作符实现
    cout << setfill('*');
    cout << left << setw(12) << a << endl;
    cout << right << setw(12) << a << endl;
    cout << hex << a << '\t' << dec << a << '\t' << oct << a << endl;
    cout << scientific << b << '\t' << fixed << b << endl;

}
/*
    将两个文件合并成一个文件。
    */

void test02()
{
    int i = 1;
    char c[1000];
    ifstream ifile1("d:/1.cpp");
    ifstream ifile2("d:/2.cpp");
    ofstream ofile("d:/3.cpp");
    //流对象的成员函数eof的值为非0值(一般设为1),表示文件结束 了。
    while (!ifile1.eof())
    {
        ifile1.getline(c, 999);//C++ getline()函数读入一行字符
        ofile << c << endl;
    }
    while (!ifile2.eof())
    {
        ifile2.getline(c, 999);
        ofile << c << endl;
    }

    ifile1.close();
    ifile2.close();
    ofile.close();

}
/*
    统计一篇英文文章中单词的个数与行数
    */
//isalpha() 判断字符ch 是否为英文字母,若为英文字母
//返回非0(小写字母2 大写字母1),不是字母返回0
#include<cctype>
bool isalph(char);
bool isalph(char c){
    return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
}

void test03()
{

    ifstream ifile("d:/2.cpp");
    if (!ifile)
    {
        cout << "读文件失败" << endl;
    }
    char text[1000];
    bool inword = false;
    int rows = 0, words = 0;
    int i;

    while (!ifile.eof())
    {
        ifile.getline(text, 999);//读一行
        rows++;

        i = 0;
        while (text[i] != 0)
        {
            if (!isalph(text[i]))//检测到空格 hello world  
                inword = false;
            else if (isalph(text[i]) && inword == false)
            {
                words++;
                inword = true;
            }
            i++;
        }
    }

    cout << "rows = " << rows << endl;
    cout << "words = " << words << endl;
    ifile.close();

}
/*
    将C++源程序每行前加上行号与一个空格
    */
void test04()
{
    int i = 1;
    char c[1000];
    ifstream ifile("d:/1.cpp");//读文件
    ofstream ofile("d:/2.cpp");//写文件

    while (!ifile.eof())
    {
        ofile << i++ << ":";
        ifile.getline(c, 999);
        ofile << c << endl;
    }
    ifile.close();
    ofile.close();
}

/*
    输出 ASCII码值从20到127的ASCII码字符表,格式为每行10个
    */
void test05()
{
    int i, j =0;
    for (i = 32; i < 127; i++)
    {
        cout << char(i) << " ";
        j++;
        if (j % 10 == 0)
            cout << endl;
    }
}
void test()
{
    //test01();
    //test02();
    //test03();
    //test04();
    test05();
}
#endif

/*
    STL模板基本概念
    algorithm(算法)、container(容器)和iterator(迭代器)

    1、STL的一个重要特点是数据结构和算法的分离
    */
#include <vector>
#include <algorithm>
#if 0
//容器装 基础数据类型变量
void test01()
{
    vector<int> v1;//容器 把你的元素copy到容器中
    v1.push_back(-1);
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);
    v1.push_back(3);
    // 1  3  5
    //▲
    //迭代器:相当于一个指针
    for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
    {
        cout << *it << " ";
    }

    //3 算法 算法和迭代器 进行无缝的连接
    int num1 = count(v1.begin(), v1.end(), 3);//计算元素3 在数组中的个数
    cout << "num1: " << num1 << endl;

}

/*
    由于容器元素的存放是按值复制的方式进行的,所以此时Teacher
    必须提供Teacher的拷贝构造函数,以保证Teacher对象间拷贝正常。

    */
class Teacher
{
public:
    int age;
    char name[64];//若是指针,必须考虑深拷贝和浅拷贝问题
public:
    void prinT()
    {
        cout << "age: " << age << endl;
    }
};
//容器装 类对象
void test02()
{
    Teacher t1, t2, t3;
    t1.age = 31;
    t2.age = 32;
    t3.age = 33;

    vector<Teacher> v1;//1容器 把你的元素copy到容器中 容器实现了数据类型和算法的有效分离
    v1.push_back(t1);
    v1.push_back(t2);
    v1.push_back(t3);

    // 1  3  5
    //▲
    //迭代器:相当于一个指针
    for (vector<Teacher>::iterator it = v1.begin(); it != v1.end(); it++)
    {
        cout << it->age<< " ";
    }

    //3 算法 算法和迭代器 进行无缝的连接
    //int num1 = count(v1.begin(), v1.end(), 3);
    //cout << "num1: " << num1 << endl;
    //抛砖: 如何求 容器 中 老师结点的 年龄=33 个数....
}

//容器中装 类对象指针
void test03()
{
    Teacher t1, t2, t3;
    t1.age = 31;
    t2.age = 32;
    t3.age = 33;

    Teacher *p1, *p2, *p3;
    p1 = &t1;
    p2 = &t2;
    p3 = &t3;
    //1容器: 把t1 t2 t3内存首地址 放入到了容器
    vector<Teacher *> v1;
    v1.push_back(p1);
    v1.push_back(p2);
    v1.push_back(p3);
    // p1  p2  p3
    //           ▲
    //2迭代器: 相当于一个指针 分类
    for (vector<Teacher*>::iterator it = v1.begin(); it != v1.end(); it++)
    {
        cout << (*it)->age << endl;//注意是二级指针
    }
}
void  test()
{
    //test01();
    //test02();
    test03();
}

#endif

/*
    STL中的string
       string是STL的字符串类型,通常用来表示字符串
       string是一个类, char*是一个指向字符的指针。
    string封装了char*,管理这个字符串,是一个char*型的容器

    */
#if 0
/*
   默认构造函数:
string();      //构造一个空的字符串string s1。
   拷贝构造函数:
string(const string &str);  //构造一个与str一样的string。如string s1(s2)。
   带参数的构造函数
string(const char *s);     //用字符串s初始化
string(int n,char c);      //用n个字符c初始化
*/
//string 的初始化
void test01()
{
    string s0 = string();   //构造一个空串
    string s1 = "aaaa";     //初始化法1
    string s2("bbbb");      //初始化法2
    string s3 = s2;         //通过copy构造函数
    string s4(10, 'n');     //用10个字符n初始化10
    cout << "s1:" << s1 << endl;
    cout << "s2:" << s2 << endl;
    cout << "s3:" << s3 << endl;
    cout << "s4:" << s4 << endl;
}

/*
    string 的遍历 返回字符串中的第n个字符

    string类的字符操作:
    const char & operator[] (int n) const;//最后一个const修饰的是this指针
    const char & at(int n) const;
    char & operator[] (int n);
    char &at(int n);

    perator[]和at()均返回当前字符串中第n个字符,但二者是有区别的。
    主要区别在于at()在越界时会抛出异常,[]在刚好越界时会返回(char)0,
    再继续越界时,编译器直接出错。
    如果你的程序希望可以通过try,catch捕获异常,建议采用at()
    */
void test02()
{
    string s1 = "abcdefg";
    //1 数组方式
    for (int i = 0; i < s1.length(); i++)
    {
        cout << s1[i] << " ";
    }
    cout << endl;

    //2 迭代器
    for (string::iterator it = s1.begin(); it != s1.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
    try
    {
        for (int i = 0; i < s1.length() + 3; i++)
        {
            cout << s1.at(i) << " ";
        }
    }
    catch (...)
    {
        cout << "发生异常" << endl;
    }
    cout << "at之后" << endl;
    /*
    try
    {
    for (int i=0; i<s1.length() + 3; i++)
    {
    cout << s1[i] << " "; //出现错误 不向外面抛出异常 引起程序的中断
    }
    }
    catch ( ... )
    {
    cout << "发生异常\n" ;
    }
    */
}

/*
    字符指针和string 的转换
    1、从string取得const char*的操作
       const char *c_str() const;   //返回一个以'\0'结尾的字符串的首地址
    2、把string拷贝到char*指向的内存空间的操作
       int copy(char *s, int n, int pos=0) const;
    把当前串中以pos开始的n个字符拷贝到以s为起始位置的字符数组中,
    返回实际拷贝的数目。
    注意要保证s所指向的空间(要分配内存)足够大以容纳当前字符串,
    不然会越界
    */
void test03()
{
    //1 char * ===>string
    string s1 = "hello world";
    //2 string ===>char *
    printf("s1: %s\n", s1.c_str());//漏出指针

    //3 s1的内容 copy到buf中
    char buf1[128] = {0};
    s1.copy(buf1, 3, 0);//注意 只给你copy 3 个字符串 不会变成c风格的字符串
    cout << "buf1: " << buf1 << endl;//打印hel
}

/*
    字符串的连接
    string &operator+=(const string &s);    //把字符串s连接到当前字符串结尾
    string &operator+=(const char *s);      //把字符串s连接到当前字符串结尾
    string &append(const char *s);          //把字符串s连接到当前字符串结尾
    string &append(const char *s,int n);    //把字符串s的前n个字符连接到当前字符串结尾
    string &append(const string &s);        //同operator+=()

    string &append(const string &s,int pos, int n);//把字符串s中从pos开始的n个字符连接到当前字符串结尾
    string &append(int n, char c);          //在当前字符串结尾添加n个字符c

    */
void test04()
{
    string s0 = "7890";
    string s1 = "aaa";
    string s2 = "bbb";

    s1 = s1 + s2;//法1
    s1 += s0;
    s1+=("haha");

    cout << "s1:" << s1 << endl;

    string s3 = "333";
    string s4 = "444";
    string s5 = "asdfghk";
    s3.append(s4);
    s3.append(s5, 0, 3); //把字符串s5中从0开始的3个字符连接到当前字符串结尾
    s3.append(3, '*');   //在当前字符串结尾添加3个字符*
    s3.append("zxcvbn", 3);把字符串s的前n个字符连接到当前字符串结尾
    cout << "s3: " << s3 << endl;
}

/*
    字符串的查找和替换 重点
    查找
    int find(char c,int pos=0) const;            //从pos开始查找 字符c   在当前字符串的位置
    int find(const char *s, int pos=0) const;    //从pos开始查找 字符串s 在当前字符串的位置,如果找到,则返回
    该字符串首次出现时其首字符的索引;否则返回string::npos

    int find(const string &s, int pos=0) const;  //从pos开始查找 字符串s 在当前字符串的位置
    find函数如果查找不到,就返回-1

    int rfind(char c, int pos=npos) const;        //从pos开始从后向前查找字符c在当前字符串中的位置
    int rfind(const char *s, int pos=npos) const;
    int rfind(const string &s, int pos=npos) const;
    rfind是反向查找的意思,如果查找不到, 返回-1

    替换
    string &replace(int pos, int n, const char *s);    //删除从pos开始的n个字符,然后在pos处插入串s
    string &replace(int pos, int n, const string &s);  //删除从pos开始的n个字符,然后在pos处插入串s
    void swap(string &s2);    //交换当前字符串与s2的值

    */
void test05()
{
    string s1 = "wbm hello wbm 111  wbm 222  wbm 333 ";
    //           ▲
    //第一次 出现wbm index
    int index = s1.find("wbm", 0);//位置从下标0开始查找字符串第一次出现的位置
    cout << "index: "<< index << " ";
    //案例1 求wbm出现的次数 每一次出现的数组下标
    int offindex = s1.find("wbm", 0);

    while (offindex != string::npos)
    {
        cout << "offindex:" << offindex << endl;
        offindex = offindex + 1;
        offindex = s1.find("wbm", offindex);
    }

    //案例2 把小写wbm 替换 为 WBM

    string s3 = "aaa bbb ccc";
    s3.replace(0, 3, "AAA");//删除从0位置开始的3个字符,然后用AAA代替
    cout << "s3: " << s3 << endl;

    offindex = s1.find("wbm", 0);
    while (offindex != string::npos)
    {
        cout << "offindex:" << offindex << endl;
        s1.replace(offindex, 3, "WBM");
        offindex = offindex + 1;
        offindex = s1.find("wbm", offindex);
    }
    cout << "s1替换后的结果: " << s1 << endl;
}

/*
    截断(区间删除)和插入
    string &insert(int pos, const char *s);
    string &insert(int pos, const string &s);
    //前两个函数在pos位置插入字符串s

    string &insert(int pos, int n, char c);  //在pos位置 插入n个字符c
    string &erase(int pos=0, int n=npos);    //删除pos开始的n个字符,返回修改后的字符串

    */
void test06()
{
    string s1 = "HELLO1 HELLO2 HELLO3";
    string::iterator it = find(s1.begin(), s1.end(), 'L');//使用算法find()
    if (it != s1.end())
    {
        s1.erase(it);//删除指针所指向字符 删除一个字符
    }
    cout << "s1删除L后的结果: " << s1 << endl;
    s1.erase(s1.begin(), s1.end());
    cout << "s1 全部删除: " << s1 << endl;
    cout << "s1 长度: " << s1.length() << endl;

    string s2 = "BBB";
    s2.insert(0, "AAA");//头插法
    s2.insert(s2.length(), "CCC");//尾插法
    cout << "s2" << s2 << endl;

}

void test07()
{
    string s1 = "aaaBBB";
    //transform() 第四个参数可以是 函数的入口地址 函数对象 预定义的函数对象
    //第三个参数是转换的结果放在那里,也可以指定别的内存空间 
    transform(s1.begin(), s1.end(),s1.begin(), toupper);//使用预定义的函数对象
    cout << "s1: " << s1 << endl;

    string s2 = "AAAbbbbbb";
    transform(s2.begin(), s2.end(), s2.begin(), tolower);
    cout << "s2: " << s2 << endl;

}
void test()
{
    //test01();
    //test02();
    //test03();
    //test04();
    //test05();
    //test06();
    test07();
}

#endif

/*
    vector操练
    1、vector是将元素置于一个动态数组中加以管理的容器。
    vector可以随机存取元素(支持索引值直接存取, 用[]操作符或at()方法)。
    vector尾部添加或移除元素非常快速。但是在中部或头部插入元素或移除元素比较费时

    2、vector对象的默认构造
    vector采用模板类实现,vector对象的默认构造形式
    vector<T> vecT;

    vector<int> vecInt;         //一个存放 int      的vector容器。
    vector<float> vecFloat;     //一个存放 float    的vector容器。
    vector<string> vecString;   //一个存放 string   的vector容器。
    ...                         //尖括号内还可以设置指针类型或自定义类型。
    Class CA{};
    vector<CA*> vecpCA;     //用于存放CA对象的指针的vector容器。
    vector<CA> vecCA;       //用于存放CA对象的vector容器。

    注意:由于容器元素的存放是按值复制的方式进行的,所以此时CA必须提供CA的
    拷贝构造函数,以保证CA对象间拷贝正常。

    */
#if 0
/*
    数组元素的添加和删除
    */
void test01()
{
    vector<int> v1;

    cout << "length: " << v1.size() << endl;//获取数组的大小
    v1.push_back(1);//向数组的尾部添加元素
    v1.push_back(2);
    v1.push_back(3);
    cout << "length: " << v1.size() << endl;//获取数组的大小
    cout << "头部元素:" << v1.front() << endl;//获取头部元素

    //v1.front()获取头部元素,既可以当左值,也可以当右值
    //v1.back() 获取头部元素,既可以当左值,也可以当右值
    //函数返回值当左值 应该返回一个引用
    v1.front() = 11;    //修改头部元素
    v1.back() = 55;     //修改尾部元素

    while (v1.size() > 0)
    {
        cout << "尾部元素" << v1.back() << endl;//获取尾部元素
        v1.pop_back();//删除尾部元素
    }
}

/*
    vector的初始化

    vector(beg,end);  //构造函数将[beg, end)区间中的元素拷贝给本身。
    注意该区间是左闭右开的区间。
    vector(n,elem);   //构造函数将n个elem拷贝给本身。
    vector(const vector &vec);  //拷贝构造函数
    */
void test02()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);
    v1.push_back(7);

    vector<int> v2 = v1;//对象初始化
    vector<int> v3(v1.begin(), v1.begin() + 3);//0+3 =3  0 1 2 共3个元素
    vector<int> v4(v1.begin(), v1.end());// [begin, end)
    vector<int> v5(v1);
    vector<int> v6(3,5);//容器v6存在3 个元素,每个元素都是5

    vector<int> va, vb, vc, vd;
    int iarray[] = { 0, 1, 2, 3, 4 };
    va.assign(iarray, iarray + 5);//注意是[iarray, iarray + 5) 左闭右开区间
    vb.assign(va.begin(), va.end());

    vc.assign(3, 9);//将3 个元素9 赋值给本身
    vd = va;
    va.swap(vd);//将vd与va的元素互换
}
/*
    vector的遍历 通过数组的方式
    */
void printV(vector<int> &v)
{
    for (int i = 0; i < v.size(); i++)
    {
        cout << v[i] << " ";
    }

}
/*
   vector.size();       //返回容器中元素的个数
   vector.empty();      //判断容器是否为空
   vector.resize(num);  //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
   vector.resize(num, elem);  //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除


*/
void test03()
{
    vector<int> v1(10);//提前把内存准备好
    for (int i = 0; i < 10; i++)
    {
        v1[i] = i + 1;//[] 重载了
    }
    for (int i = 0; i < 10; i++)
    {
        cout << v1[i] << " ";//[] 重载了
    }
    printV(v1);

    vector<int> v2;
    v2.assign(v1.begin(), v1.begin() + 3);

    int isize = v2.size();
    cout << "容器的大小: "<<v2.size() << endl;

    bool bempty = v2.empty();
    cout << "bempty = " << bempty;

    v2.resize(5);
    printV(v2);
    v2.resize(8,66);
    printV(v2);

}
/*
    push_back 的强化记忆
    */
void test04()
{
    vector<int> v1(10);//提前把内存准备好,默认初始化为0
    v1.push_back(100); //在数组后添加
    v1.push_back(200);
    cout << "size: " << v1.size() << endl;
    printV(v1);
}

/*
    vector 迭代器
    1迭代器 end()的理解
    1   3   5
    ▲
    ▲
    当 it == v1.end()的时候 说明这个容器已经遍历完毕了...
    注意:end()的位置 应该是 5的后面

    2 迭代器的种类
    typedef iterator pointer;
    typedef const_iterator const_pointer;
    typedef _STD reverse_iterator<iterator> reverse_iterator;
    typedef _STD reverse_iterator<const_iterator> const_reverse_iterator;
    */
void test05()
{
    vector<int> v1(10);
    for (int i = 0; i < 10; i++)
    {
        v1[i] = i + 1;
    }
    //正向遍历
    for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl<<"------------" << endl;
    //逆向遍历
    for (vector<int>::reverse_iterator rit = v1.rbegin(); rit != v1.rend(); rit++)
    {
        cout << *rit << " ";
    }
}

/*
    vector 删除 重点
       vector.clear(); //移除容器的所有数据
       vec.erase(beg,end);  //删除[beg,end)区间的数据,返回下一个数据的位置。
       vec.erase(pos);    //删除pos位置的数据,返回下一个数据的位置

    vector 的插入
    vector.insert(pos,elem);   //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
    vector.insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
    vector.insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值

    */
void test06()
{
    vector<int> v1(10);
    for (int i = 0; i < 10; i++)
    {
        v1[i] = i + 1;
    }

    //区间删除
    v1.erase(v1.begin(), v1.begin() + 3);
    printV(v1);

    //根据元素的位置 指定位置删除
    v1.erase(v1.begin());//在头部删除一个元素
    printV(v1);
    cout << endl;

    //根据元素的值删除
    v1[1] = 2;
    v1[3] = 2;
    printV(v1);

    for (vector<int>::iterator it = v1.begin(); it != v1.end();)//这里不能有it++了
    {
        if (*it == 2)
        {
            //当删除迭代器所指向的元素的时候,erase删除函数会让
            //it自动下移动,返回新的位置
            it = v1.erase(it);
        }
        else
        {
            it++;
        }
    }
    printV(v1);
    cout << endl;

    //在开头添加100, 在末尾添加200
    v1.insert(v1.begin(), 100);
    v1.insert(v1.end(), 200);
    printV(v1);
    cout << endl;

    //在开头添加2个 99
    v1.insert(v1.begin(), 2, 99);
    printV(v1);
    cout << endl;

    //在开头添加[ v1.begin()+3, v1.end() )区间的数据
    v1.insert(v1.begin(), v1.begin()+3, v1.end());
    printV(v1);
    cout << endl;

    //在开头删除[v1.begin(), v1.begin() + 3) 区间的数据
    v1.erase(v1.begin(), v1.begin() + 3);
    printV(v1);
    cout << endl;

    //删除 v1.begin() + 2 位置的数据
    v1.erase(v1.begin() + 5);
    printV(v1);
    cout << endl;

    //移除容器所有数据
    v1.clear();
    printV(v1);
    cout << endl;
}

/*
    vector 数据存取
    vec.at(idx); //返回索引idx所指的数据,如果idx越界,抛出out_of_range异常。
    vec[idx];    //返回索引idx所指的数据,越界时,运行直接报错

    */
void test07()
{
    vector<int> v1(10);
    for (int i = 0; i < 10; i++)
    {
        v1[i] = i + 1;
    }

    try
    {
        v1.at(2) = 111;
        v1[5] = 555;
    }
    catch (out_of_range &v1)
    {
        cout << "数组越界" << endl;
    }
    printV(v1);

    try
    {
        v1.at(11) = 111;//抛异常
        //v1[12] = 555;//程序崩溃
    }
    catch (out_of_range &v1)
    {
        cout << "数组越界" << endl;
    }
    printV(v1);

}
void test()
{
    //test01();
    //test02();
    //test03();
    //test04();
    //test05();
    test06();
    //test07();
}
#endif

#include<deque>
/*
    deque 即双端队列。
    (deque,全名double-ended queue)是一种具有队列和栈的性质的数据结构。
    双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。

    deque容器
       deque是“double-ended queue”的缩写,和vector一样都是STL的容器,deque是双端数组,而vector是单端的。
       deque在接口上和vector非常相似,在许多操作的地方可以直接替换。
       deque可以随机存取元素(支持索引值直接存取, 用[]操作符或at()方法,这个等下会详讲)。
       deque头部和尾部添加或移除元素都非常快速。但是在中部安插元素或移除元素比较费时

    */
#if 0

void printD(deque<int> &d)
{
    for (deque<int>::iterator it = d.begin(); it != d.end(); it++)
    {
        cout << *it << " ";
    }
}
void test01()
{
    deque<int> dl;
    dl.push_back(1);
    dl.push_back(3);
    dl.push_back(5);

    dl.push_front(-11);
    dl.push_front(-12);
    dl.push_front(-33);
    dl.push_front(-55);
    dl.push_front(-75);
    cout << endl;

    cout << "头部元素:" << dl.front() << endl;
    cout << "尾部元素:" << dl.back() << endl;
    printD(dl);
    cout << endl;

    dl.pop_front(); //删除头部元素
    dl.pop_back();  //删除尾部元素
    printD(dl);
    cout << endl;

    //查找 -33在数组下标的值
    deque<int>::iterator it = find(dl.begin(), dl.end(), -33);
    if (it != dl.end())
    {
        cout << "-33 的数组下标是" << distance(dl.begin(), it) << endl;
    }
    else
    {
        cout << "没有找到值为 -33 的元素" << endl;
    }
}

/*
    //que.assign(beg,end) 将[beg; end)区间中的数据赋值给que。
    //que.assign(n,elem) 将n个elem的拷贝赋值给que。
    //que.at(idx) 传回索引idx所指的数据,如果idx越界,抛出out_of_range。
    //que.rbegin() 返回一个逆序迭代器,它指向容器que的最后一个元素。
    //que.rend() 返回一个逆序迭代器,它指向容器que的第一个元素的前一个位置。
    //que.resize(num) 重新指定队列的长度。
    //que.swap(que2) 交换容器que和que2中的所有元素。
    //swap(que1,que2) 交换容器que1和que2中的所有元素。
    //que.erase(pos) 删除pos位置的数据,传回下一个数据的位置。
    //que.erase(beg,end) 删除[beg,end)区间的数据,传回下一个数据的位置。
    //que.insert(pos,elem) 在pos位置插入一个elem拷贝,传回新数据位置
    //que.insert(pos,beg,end) 在pos位置插入在[beg,end)区间的数据。无返回值

    que.back()  返回容器que的最后一个元素的引用。如果que为空,则该操作未定义。
    que.begin() 传回迭代器中的第一个数据地址。
    que.clear() 移除容器中所有数据。
      que.empty() 判断容器是否为空。
        que.end()       返回一个迭代器,它指向容器que的最后一个元素的下一位置。 
        que.front()     返回容器que的第一个元素的引用。如果que为空,则该操作为空。
        que.insert(pos,n,elem) 在pos(迭代器)位置插入>n个elem数据。无返回值
        que.max_size()  返回容器que可容纳的最多元素个数。
          que.pop_back()    删除最后一个数据。
            que.pop_front() 删除头部数据。
            que.push_back(elem)     在尾部加入一个数据。
            que.push_front(elem)    在头部插入一个数据。
            que.size()  返回容器中实际数据的个数。
            */
//数据存取
void test02()
{
    deque<int> d;
    d.push_back(1);
    d.push_back(3);
    d.push_back(5);
    d.push_back(7);
    d.push_back(9);

    int da0 = d.at(0);
    int da1 = d[1];
    cout << "da0 :" << da0 << endl;
    cout << "da1 :" << d[1] << endl;

    d.at(0) = 99;
    d[1] = 88;

    printD(d);
    int dfront = d.front();
    int dback = d.back();

    d.front() = 77;
    d.back() = 66;
    printD(d);
}
/*
    deque 与迭代器
       deque.begin();  //返回容器中第一个元素的迭代器。
       deque.end();    //返回容器中最后一个元素 之后 的迭代器。
       deque.rbegin(); //返回容器中倒数第一个元素的迭代器。
       deque.rend();   //返回容器中倒数最后一个元素之后的迭代器
    */
void test03()
{
    deque<int> d;
    d.push_back(1);
    d.push_back(3);
    d.push_back(5);
    d.push_back(7);
    d.push_back(9);

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

    for (deque<int>::reverse_iterator it = d.rbegin(); it != d.rend(); it++)
    {
        cout << *it << " ";
    }
}
/*
    deque 的带参数构造
    deque(beg,end);   //构造函数将[beg, end)区间中的元素拷贝给本身。注意该区间是左闭右开的区间。
    deque(n,elem);   //构造函数将n个elem拷贝给本身。
    deque(const deque  &deq);  //拷贝构造函数

    */
void test04()
{
    deque<int> d;
    d.push_back(1);
    d.push_back(3);
    d.push_back(5);
    d.push_back(7);
    d.push_back(9);

    deque<int> db(d.begin(), d.end());
    printD(db);
    cout << endl;
    deque<int>dc(5, 8);
    printD(dc);
    cout << endl;

    deque<int> f(d);
    printD(f);
    cout << endl;
}

/*
    deque 的赋值
    deque.assign(beg,end);  //将[beg, end)区间中的数据拷贝赋值给本身。注意该区间是左闭右开的区间。
    deque.assign(n,elem);   //将n个elem拷贝赋值给本身。
    deque& operator=(const deque &deq); //重载等号操作符
    deque.swap(deq);   //将vec与本身的元素互换

    */
void test05()
{
    deque<int> d, da, db, dc,df;
    d.push_back(1);
    d.push_back(3);
    d.push_back(5);
    d.push_back(7);
    d.push_back(9);

    da.assign(d.begin(), d.begin() + 2);
    printD(da);
    cout << endl;

    db.assign(5, 8);//将5 个8 赋值给本身
    printD(db);
    cout << endl;

    dc = d;
    printD(dc);
    cout << endl;

    df.swap(d);
    printD(df);
    cout << endl;
}
/*
    deque 的大小
    deque.size();      //返回容器中元素的个数
    deque.empty();     //判断容器是否为空

    //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
    //如果容器变短,则末尾超出容器长度的元素被删除。
    deque.resize(num); 

    //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
    //如果容器变短,则末尾超出容器长度的元素被删除
    deque.resize(num, elem); 

    */
void test06()
{
    deque<int> d;
    d.push_back(1);
    d.push_back(3);
    d.push_back(5);
    int size = d.size();
    cout <<"size = "<< size << endl;
    cout << d.empty() << endl;
    if (!d.empty())
    {
        d.resize(5);
        printD(d);
        cout << endl;

        d.resize(7, 1);
        printD(d);
        cout << endl;

        d.resize(2);
        printD(d);
        cout << endl;
    }
}

/*
    deque 的插入
    deque.insert(pos,elem);      //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
    deque.insert(pos,n,elem);   //在pos位置插入n个elem数据,无返回值。
    deque.insert(pos,beg,end);   //在pos位置插入[beg,end)区间的数据,无返回值
    deque.clear(); //移除容器的所有数据
    deque.erase(beg,end);  //删除[beg,end)区间的数据,返回下一个数据的位置。
    deque.erase(pos);      //删除pos位置的数据,返回下一个数据的位置。

    */
void test07()
{
    deque<int> d;
    d.push_back(1);
    d.push_back(3);
    d.push_back(5);
    d.push_back(6);
    d.push_back(9);
    d.push_back(3);
    d.push_back(33);

    deque<int>::iterator itbegin = d.begin() + 1;
    deque<int>::iterator itend = d.begin() + 3;

    d.erase(itbegin, itend);
    printD(d);

    for (deque<int>::iterator it = d.begin(); it != d.end();)//小括号里不需写  ++it
    {
        if (*it == 3)
        {
            it = d.erase(it);//以迭代器为参数,删除元素3,并把数据删除后的下一个元素位置返回给迭代器。
        }
        else
        {
            ++it;
        }
    }
    cout << endl;
    printD(d);
    d.clear();
}
void test()
{
    //test01();
    //test02();
    //test03();
    //test04();
    //test05();
    //test06();
    test07();


}

#endif

/*
   stack是堆栈容器,是一种“先进后出”的容器。
   stack是简单地装饰deque容器而成为另外的一种容器。
   #include <stack>
stack.push(elem);//往栈头添加元素
stack.pop();     //从栈头移除第一个元素
*/
#include <stack>
#if 0

//栈模型  先入后出
//栈的算法 和 数据类型的分离

/*
   stack.top();    //返回最后一个压入栈元素
   stack.empty();  //判断堆栈是否为空
   stack.size();   //返回堆栈的大小
*/
void test01()
{
    stack<int> s;
    //入栈
    for (int i = 0; i < 10; i++)
    {
        s.push(i + 1);
    }
    cout << "栈的大小" << s.size() << endl;

    //出栈
    while (!s.empty())
    {
        int tmp = s.top();//获取栈顶元素
        cout << tmp << " ";
        s.pop();//弹出栈顶元素
    }
}

class Teacher
{
public:
    int age;
    char name[32];
public:
    void printT()
    {
        cout << "age: " << age << endl;
    }
};

void test02()
{
    Teacher t1, t2, t3;
    t1.age = 11;
    t2.age = 32;
    t3.age = 33;

    stack<Teacher> s;
    s.push(t1);
    s.push(t2);
    s.push(t3);
    while (!s.empty())
    {
        Teacher tmp = s.top();
        tmp.printT();
        s.pop();
    }
}

void test03()
{
    Teacher t1, t2, t3;
    t1.age = 11;
    t2.age = 32;
    t3.age = 33;

    stack<Teacher *> s;
    s.push(&t1);
    s.push(&t2);
    s.push(&t3);
    while (!s.empty())
    {
        Teacher * tmp = s.top();
        tmp->printT();
        s.pop();
    }
}

/*
    stack对象的拷贝构造与赋值
    stack(const stack &stk);             //拷贝构造函数
    stack& operator=(const stack &stk); //重载等号操作符
    */
void test04()
{
    stack<int> s;
    s.push(1);
    s.push(2);
    s.push(3);

    stack<int> sa(s);//拷贝构造
    stack<int> sb;
    sb = s;//赋值


}
void test()
{
    //test01();
    //test02();
    //test03();
    test04();
}
#endif

/*
   queue是队列容器,是一种“先进先出”的容器。
   queue是简单地装饰deque容器而成为另外的一种容器

queue采用模板类实现,queue对象的默认构造形式:queue<T> queT;
*/
#include <queue>
#if 0

/*
    队列中的基本数据类型
    queue.push(elem);   //往队尾添加元素
    queue.pop();    //从队头移除第一个元素
    queue.back();   //返回最后一个元素
    queue.front();  //返回第一个元素
    queue.empty();  //判断队列是否为空
    queue.size();   //返回队列的大小
    */
void test01()
{
    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];
public:
    void printT()
    {
        cout << "age:" << age << endl;
    }
};
void test02()
{
    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();//从队头移除第一个元素
    }
}

void test03()
{
    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();
    }
}
void test()
{
    //test01();
    //test02();
    test03();
}

#endif

/*
   list是一个 双向链表 容器,可高效地进行插入删除元素。
   list不可以随机存取元素,所以不支持at.(pos)函数与[]操作符。
it++(ok) it+5(err)

list.push_back(elem);   //在容器尾部加入一个元素
list.pop_back();        //删除容器中最后一个元素
list.push_front(elem);  //在容器开头插入一个元素
list.pop_front();       //从容器开头移除第一个元素

list.front();   //返回第一个元素。
list.back();    //返回最后一个元素
list.begin();   //返回容器中第一个元素的迭代器。
list.end();     //返回容器中最后一个元素之后的迭代器。
list.rbegin();  //返回容器中倒数第一个元素的迭代器。
list.rend();    //返回容器中倒数最后一个元素的后面的迭代器

list(beg,end);  //构造函数将[beg, end)区间中的元素拷贝给本身。注意该区间是左闭右开的区间。
list(n,elem);   //构造函数将n个elem拷贝给本身。
list(const list &lst);  //拷贝构造函数。

list.assign(beg,end);   //将[beg, end)区间中的数据拷贝赋值给本身。注意该区间是左闭右开的区间。
list.assign(n,elem);    //将n个elem拷贝赋值给本身。
list& operator=(const list &lst);   //重载等号操作符
list.swap(lst);  // 将lst与本身的元素互换

list.size();    //返回容器中元素的个数
list.empty();   //判断容器是否为空
list.resize(num);   //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
list.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除

list.insert(pos,elem);      //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
list.insert(pos,n,elem);    //在pos位置插入n个elem数据,无返回值。
list.insert(pos,beg,end);   //在pos位置插入[beg,end)区间的数据,无返回值

list.clear();       //移除容器的所有数据
list.erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。
list.erase(pos);    //删除pos位置的数据,返回下一个数据的位置。
lst.remove(elem);   //删除容器中所有与elem值匹配的元素。

lst.reverse();      //反转链表


*/
#include <list>
#include<functional>//因为用了greater<int>() 

#if 0
//list基本操作
void test01()
{
    list<int> l;
    cout << "list 的大小: " << l.size() << endl;

    for (int i = 0; i < 10; i++)
    {
        l.push_back(i);//尾部插入元素
    }
    cout << "list的大小: " << l.size() << endl;

    //注意迭代器的使用
    list<int>::iterator it = l.begin();
    while (it != l.end())
    {
        cout << *it << " ";
        it++;//迭代器只能这么递增
    }
    cout << endl;

    //list不能随机访问
    // 0  1  2  3  4  5
    //          △
    it = l.begin();
    it++;
    it++;
    it++;
    //it = it + 5;//不支持随机访问容器
    l.insert(it, 100);//注意100插入在哪一个位置
    for (list<int>::iterator it = l.begin(); it != l.end(); it++)
    {
        cout << *it << " ";
    }
    //结论 1 
    //链表的节点index 序号从0号位置开始
    //在3号位置插入元素,让原来的3号位置变成4号位置
    //原来的4号位置变成5号位置
}

//list删除
void test02()
{
    list<int> l;
    cout << "list 的大小: " << l.size() << endl;
    for (int i = 0; i < 10; i++)
    {
        l.push_back(i);//尾部插入元素 尾插法
    }
    cout << "list 的大小: " << l.size() << endl;
    //遍历list双向链表
    for (list<int>::iterator it = l.begin(); it != l.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;

    //删除区间内的元素,只能按照以下做法,因为list不能随机存取
    //0  1  2   3   4   5
    //          ▲
    list<int>::iterator it1 = l.begin();
    list<int>::iterator it2 = l.begin();
    it2++;
    it2++;
    it2++;
    l.erase(it1, it2);//擦除的是[0, 3) 左闭右开区间
    for (list <int>::iterator it = l.begin(); it != l.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
    l.insert(l.begin(), 100);//在队列开头插入100
    l.insert(l.begin(), 100);
    l.insert(l.begin(), 100);
    l.insert(l.begin(), 3);
    l.insert(l.end(), 3);
    l.erase(l.begin());//擦除头部元素
    //法1
    l.remove(100);//移除所有与100相同的元素
    cout << "移除所有的值为100的元素:" << endl;
    for (list <int>::iterator it = l.begin(); it != l.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl<<"移除所有的值为3的元素:" << endl;
    //法2
    for (list<int>::iterator it = l.begin(); it != l.end();)
    {
        if (*it == 3)
        {
            it = l.erase(it);//以迭代器为参数,删除元素3,并把数据删除后的下一个元素位置返回给迭代器。
            //此时, 不执行++it
        }
        else
        {
            ++it;
        }
    }

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

    cout << endl;
    l.reverse();//list反序排列
    for (list <int>::iterator it = l.begin(); it != l.end(); it++)
    {
        cout << *it << " ";
    }
}
/*
   一、容器deque的使用方法
适合在头尾添加移除元素。使用方法与vector类似。
   二、容器queue,stack的使用方法
适合队列,堆栈的操作方式。
   三、容器list的使用方法
适合在任意位置快速插入移除元素
*/
void test()
{
    //test01();
    test02();
}

#endif

/*
   最大值优先级队列、最小值优先级队列
   优先级队列适配器 STL priority_queue

*/
#if 0
void test01()
{
    priority_queue<int> p1;//默认情况下是 最大值优先级队列
    priority_queue<int, vector<int>, less<int> > p2;//提前定义好的预定义函数 谓词
    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;

    while (p1.size() > 0)
    {
        cout << p1.top() << " ";
        p1.pop();//弹出元素
    }
}

void test02()
{
    priority_queue<int, vector<int>, greater<int> > p1;//现在编译器不能用了 
    p1.push(33);
    p1.push(11);
    p1.push(55);
    p1.push(22);
    cout << "队头元素: " << p1.top() << endl;
    cout << "队头大小: " << p1.size() << endl;

    while (p1.size() > 0)
    {
        cout << p1.top() << " ";
        p1.pop();//弹出元素
    }
}
void test()
{
    test01();
    test02();
}
#endif

/*
    set与multiset
    1、set是一个集合容器,其中所包含的元素是唯一的,集合中的元素按一定的顺序
    排列。元素插入过程是按排序规则插入,所以不能指定插入位置。
    2、set采用红黑树变体的数据结构实现,红黑树属于平衡二叉树。在插入操作和删除
    操作上比vector快。
    3、set不可以直接存取元素。(不可以使用at.(pos)与[]操作符)。
    4、multiset与set的区别:set支持唯一键值,每个元素值只能出现一次;而multiset中
    同一值可以出现多次。
    5、不可以直接修改set或multiset容器中的元素值,因为该类容器是自动排序的。
    如果希望修改一个元素值,必须先删除原有的元素,再插入新的元素。

    */
#include <set>
#if 0
/*
    集合 元素唯一 自动排序(默认情况 从小到大) 
    不能按照[]方式插入元素
    红黑树
    */
void test01()
{
    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);

    //使用迭代器遍历
    for (set<int>::iterator it = set1.begin(); it != set1.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
    //删除集合
    while (!set1.empty())
    {
        set<int>::iterator it = set1.begin();//找到头部元素
        cout << *it << " ";
        set1.erase(set1.begin());//使用erase()使it自动下移
    }
    cout <<"集合大小:"<< set1.size() << endl;
}

/*
    对基本的数据类型,set能够自动的排序
    */

void test02()
{
    set<int> set1;
    set<int, less<int>> set2;   //默认情况从小到大
    set<int, greater<int>>set3; //#include<functional>//因为用了greater<int>() 

    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 << " ";
    }
}
/*
    对自定义类型数据进行排序
    访函数
    */

class Student
{
public:
    Student(char *name, int age)
    {
        strcpy(this->name, name);
        this->age = age;
    }
public:
    char name[64];
    int age;
};

/*
    函数对象functor的用法
    1、C++提供了一个重要的实现回调函数的方法,那就是函数对象。
    functor,翻译成 函数对象,伪函数,运算符,是重载了“()”
    操作符的普通类对象。从语法上讲,它与普通函数行为类似。
    2、greater<>与less<>就是函数对象。
    3、容器就是调用函数对象的operator()方法去比较两个值的大小
    */
struct FuncStudent
{
    //重载()
    bool operator()(const Student &left, const Student &right)
    {
        //如果左边的小 就返回真 从小到大按照年龄进行排序
        if (left.age < right.age)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
};

/*
    注意 函数运行结果显示s5插入失败,但没有提示
    */
void test03()
{
    Student s1("s1", 31);
    Student s2("s2", 22);
    Student s3("s3", 44);
    Student s4("s4", 11);
    Student s5("s5", 31);//如果两个31岁 能插入成功  
    //如何知道 插入 的结果
    //实际上 s5 插入失败
    //调用访函数
    set<Student, FuncStudent> set1;
    set1.insert(s1);//打断点追踪 查看函数的返回值
    set1.insert(s2);
    set1.insert(s3);
    set1.insert(s4);
    set1.insert(s5);
    //注意set<Student, FuncStudent>
    for (set<Student, FuncStudent>::iterator it = set1.begin(); it != set1.end(); it++)
    {
        cout << it->age << "\t" << it->name << endl;
    }
}

/*
   pair译为对组,可以将两个值视为一个单元。
   pair<T1,T2>存放的两个值的类型,可以不一样,如T1为int,T2为float。T1,T2也可以是自定义类型。
   pair.first是pair里面的第一个值,是T1类型。
   pair.second是pair里面的第二个值,是T2类型
*/

/*
    1、如何判断 set1.insert函数的返回值?
    2、Pair的用法:对组,一个整体的单元,存放两个类型(T1,T2,T1可与T2一样)的两个元素。
    3、单步调试追踪函数返回值 typedef pair<iterator, bool> _Pairib;
    */

void test04()
{
    Student s1("s1", 31);
    Student s2("s2", 22);
    Student s3("s3", 44);
    Student s4("s4", 11);
    Student s5("s5", 31);//如果两个31岁 能插入成功

    //如何知道 插入 的结果
    set<Student, FuncStudent> set1;
    //指定什么类型的迭代器 set<Student, FuncStudent>::iterator
    pair<set<Student, FuncStudent>::iterator, bool> pair1 = set1.insert(s1);
    if (pair1.second == true)
    {
        cout << "插入s1成功" << endl;
    }
    else
    {
        cout << "插入s1失败" << endl;
    }

    set1.insert(s2);

    pair<set<Student, FuncStudent>::iterator, bool> pair2 = set1.insert(s5);
    if (pair2.second == true)
    {
        cout << "插入s5成功" << endl;
    }
    else
    {
        cout << "插入s5失败" << endl;
    }
}

/*
    find查找 equal_range  返回结果是一个pair
    set.find(elem);  //查找elem元素,返回指向elem元素的迭代器。
    set.count(elem); //返回容器中值为elem的元素个数。对set来说,要么是0,要么是1。对multiset来说,值可能大于1。
    set.lower_bound(elem);  //返回第一个 >=elem 元素的迭代器。
    set.upper_bound(elem);  //返回第一个 >elem  元素的迭代器。
    set.equal_range(elem);  //返回容器中与elem相等的上下限的两个迭代器。上限是闭区间,下限是开区间,如[beg,end)。
    
    以上函数返回两个迭代器,而这两个迭代器被封装在pair中

    */
void test05()
{
    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 << " ";
    }
    cout << endl;
    set<int>::iterator it0 = set1.find(5);
    cout << "it0: " << *it0 << endl;
    int num1 = set1.count(5);
    cout << "num1: " << num1 << endl;

    set<int>::iterator it1 = set1.lower_bound(5);//返回大于等于5的迭代器的位置
    cout << "*it1: " << *it1 << endl;

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

    //把5 元素删除掉
    set1.erase(5);//删除容器中值为5 的元素
    /*
    typedef pair<iterator, bool> _Pairib;
    typedef pair<iterator, iterator> _Pairii;
    typedef pair<const_iterator, const_iterator> _Paircc;
    */
    pair<set<int>::iterator, set<int>::iterator>  mypair = set1.equal_range(5);
    set<int>::iterator it3 = mypair.first;  //第一个迭代器找大于等于5的
    cout << "it3:" << *it3 << endl;  //5  //6
    set<int>::iterator it4 = mypair.second; //第二个迭代器找大于5的
    cout << "it4:" << *it4 << endl;  //6  //6
}

/*
    multiset的用法

    */

void test06()
{
    multiset<int> set1;
    int tmp = 0;

    printf("请输入multiset集合的值:");
    scanf("%d", &tmp);

    while (tmp != 0)
    {
        set1.insert(tmp);
        printf("请输入multiset集合的值:");
        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);
    }
}

void test()
{
    //test01();
    //test02();
    test03();
    //test04();
    //test05();
    //test06();
}
#endif

#include <map>
/*
    map的使用
    1、map是标准的关联式容器,一个map是一个键值对序列,即(key,value)对。
    它提供基于key的快速检索能力。
    2、map中key值是唯一的。集合中的元素按一定的顺序排列。元素插入过程
    是按排序规则插入,所以不能指定插入位置。
    3、map的具体实现采用红黑树变体的平衡二叉树的数据结构。在插入操作和
    删除操作上比vector快。
    4、map可以直接存取key所对应的value,支持[]操作符,如map[key]=value
    5、multimap与map的区别:map支持唯一键值,每个键只能出现一次;而multimap中相同键可以出现多次。multimap不支持[]操作符。
    6、#include <map>



    */
#if 0
/*
    map元素的添加/遍历/删除基本操作
    map.insert(...);    //往容器插入元素,返回pair<iterator,bool>
    */

void test01()
{
    map<int, string> map1;//key-value

    //方法1 通过 pair 的方式插入对象
    map1.insert(pair<int, string>(1, "teacher01"));
    map1.insert(pair<int, string>(2, "teacher02"));

    //方法2 通过 make_pair 的方式插入对象  最方便
    map1.insert(make_pair(3, "teacher03"));
    map1.insert(make_pair(4, "teacher04"));

    //方法3 通过 value_type 的方式插入对象
    map1.insert(map<int, string>::value_type(5, "teacher05"));
    map1.insert(map<int, string>::value_type(6, "teacher06"));

    //方法4 通过数组的方式插入值  不建议使用
    map1[7] = "teacher07";//重载了[] =
    map1[8] = "teacher08";

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

    //容器元素的删除
    while (!map1.empty())
    {
        map<int, string>::iterator it = map1.begin();
        cout << it->first << "\t" << it->second << endl;
        map1.erase(it);//迭代器会自动下移
    }
}

/*
    插入的四种方法 异同
    前三种方法 返回值为pair<iterator,bool> 则,若key已经存在,则报错
    方法四                                 则,若key已经存在,则修改

    */
void test02()
{
    map<int, string> map1;
    //typedef pair<iterator, bool> _Pairib; _Pairib
    //方法1 注意如何查找函数的返回值
    pair<map<int, string>::iterator, bool> mypair1 = map1.insert(pair<int, string>(1, "teacher01"));

    //方法2
    pair<map<int, string>::iterator, bool> mypair2 = map1.insert(make_pair(5, "teacher05"));
    if (mypair2.second != true)
    {
        cout << "key05插入失败" << endl;
    }
    else
    {
        //注意如何使用
        cout << mypair2.first->first << "\t" << mypair2.first->second << endl;
    }
    //方法3
    pair<map<int, string>::iterator, bool> mypair3 = map1.insert(map<int, string>::value_type(5, "teacher55"));
    if (mypair3.second != true)
    {
        cout << "key05插入失败" << endl;
    }
    else
    {
        //注意如何使用
        cout << mypair3.first->first << "\t" << mypair3.first->second << endl;
    }
    map1.insert(map<int, string>::value_type(6, "teacher06"));

    //方法4  使用方法4,若key已经存在,则修改
    map1[7] = "teacher07";
    map1[7] = "teacher77";

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

    //容器元素的删除
    while (!map1.empty())
    {
        map<int, string>::iterator it = map1.begin();
        cout << it->first << "\t" << it->second << endl;
        map1.erase(it);
    }
}

/*
    find 使用
    */
void test03()
{
    map<int, string> map1;
    //方法1
    map1.insert(pair<int, string>(1, "teacher01"));
    map1.insert(pair<int, string>(2, "teacher02"));
    //方法2
    map1.insert(make_pair(3, "teacher03"));
    map1.insert(make_pair(4, "teacher04"));
    //方法3
    map1.insert(map<int, string>::value_type(5, "teacher05"));
    map1.insert(map<int, string>::value_type(6, "teacher06"));
    //方法4
    map1[7] = "teacher07";
    map1[8] = "teacher08";
    //容器的遍历
    for (map<int, string>::iterator it = map1.begin(); it != map1.end(); it++)
    {
        cout << it->first << "\t" << it->second << endl;
    }
    cout << "遍历结束" << endl;
    //map 查找key为100 的元素 异常处理 find 函数返回一个迭代器
    map<int, string>::iterator it2 = map1.find(100);
    if (it2 == map1.end())
    {
        cout << "key 100 的值不存在!!!" << endl;
    }
    else
    {
        cout << it2->first << "\t" << it2->second << endl;
    }

    //equal_range 的异常处理
    pair<map<int,string>::iterator, map<int, string>::iterator> mypair1 = map1.equal_range(5);
    //第一个迭代器 >= 5 的位置
    if (mypair1.first == map1.end())
    {
        cout << "第一个迭代器>=5 的位置 不存在" << endl;
    }
    else
    {
        cout << mypair1.first->first << "\t" << mypair1.first->second << endl;
    }

    //第二个迭代器 > 5 的位置
    if (mypair1.second == map1.end())
    {
        cout << "第二个迭代器 > 5 的位置 不存在" << endl;
    }
    else
    {
        cout << mypair1.second->first << "\t" << mypair1.second->second << endl;
    }
}

/*
    multimap
    1个key值可以对应多个valude  =分组技术  用于sql语句

    公司有销售部 sale (员工2名)、技术研发部 development (1人)、
    财务部 Financial (2人)
    //人员信息有:姓名,年龄,电话、工资等组成
    //通过 multimap进行 信息的插入、保存、显示
    //分部门显示员工信息

    */
class Person
{
public:
    string name;
    int age;
    string tel;
    double saly;
};
void test04()
{
    Person p1, p2, p3, p4, p5;
    p1.name = "王1";
    p1.age = 31;

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

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

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

    p5.name = "赵5";
    p5.age = 35;

    multimap<string, Person> map2;//key-->多value
    //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));
    //Financial 部门
    map2.insert(make_pair("Financial", p5));

    //遍历
    for (multimap<string, Person>::iterator it = map2.begin(); it != map2.end(); it++)
    {
        cout << it->first << "\t" << it->second.name << "\t" << it->second.age << endl;
    }

    //统计 development 部门的人数 使用count算法
    int num2 = map2.count("development");
    cout << "development 部门人数:" << num2 << endl;

    cout << "------development 部门员工信息------"<< endl;
    //查找key
    multimap<string, Person>::iterator it2 = map2.find("development");
    int tag = 0;
    while (it2 != map2.end() && tag < num2)
    {
        cout << it2->first << "\t" << it2->second.name << endl;
        it2++;
        tag++;
    }

    //age = 32的名字修改成 name32
    cout << endl << "按照条件 检索数据 进行修改" << endl;
    //遍历
    for (multimap<string, Person>::iterator it = map2.begin(); it != map2.end(); it++)
    {
        if (it->second.age == 32)
        {
            it->second.name = "name32";
        }
    }

    //遍历
    for (multimap<string, Person>::iterator it = map2.begin(); it != map2.end(); it++)
    {
        cout << it->first << "\t" << it->second.name << "\t" << it->second.age << endl;
    }
}

void test()
{
    //test01();
    //test02();
    //test03();
    test04();
}
#endif

/*
    容器共性机制研究
    1、c++模板是容器的概念
    1)容器中缓存了用户的结点
    2)结点的类,要保证结点能够插入到容器中
    一般结点类,需要提供
    (1)无参构造函数 (2)拷贝构造函数 (3)重载=操作符

    2、理论提高:所有容器提供的都是值(value)语意,而非引用(reference)语意。
    容器执行插入元素的操作时,内部实施拷贝动作。所以STL容器内存储的元素必须
    能够 被拷贝(必须提供拷贝构造函数)。
    */
#if 0
/*
    容器值(value)语意
    */

class Teacher
{
public:
    Teacher(char * name, int age)
    {
        m_pname = new char[strlen(name) + 1];
        strcpy(m_pname, name);
        m_age = age;
    }

    ~Teacher()
    {
        if (m_pname != NULL)
        {
            delete[] m_pname;
            m_pname = NULL;
            m_age = 0;
        }
    }

    //Teacher t2 = t1
    Teacher(const Teacher &obj)
    {
        //分配新的内存
        m_pname = new char[strlen(obj.m_pname) + 1];
        //拷贝数据
        strcpy(m_pname, obj.m_pname);
        m_age = obj.m_age;
    }

    //重载= 号操作符
    //t3 = t2 = t1
    Teacher & operator=(const Teacher& obj)
    {
        //1释放旧的内存
        if (this->m_pname != NULL)
        {
            delete[]m_pname;
            m_pname = NULL;
            m_age = 0;
        }
        //2重新分配内存
        this->m_pname = new char[strlen(obj.m_pname) + 1];
        //数据拷贝
        strcpy(m_pname, obj.m_pname);
        m_age = obj.m_age;
        //返回*this
        return *this;
    }

public:
    void printT()
    {
        cout << m_pname << "\t" << m_age << endl;
    }
protected:
private:
    char *m_pname;
    int m_age;
};

void test01()
{
    Teacher t1("t1", 31);
    t1.printT();
    vector<Teacher> v1;
    v1.push_back(t1);//把t1拷贝了一份 存入到容器中了....
}
void test()
{
    test01();
}

#endif

/*
    函数对象:
    重载函数调用操作符()的类,其对象常称为函数对象(function object),即它们是
    行为类似函数的对象。一个类对象,表现出一个函数的特征,就是通过
    “对象名+(参数列表)”的方式使用一个类对象,如果没有上下文,完全可以把它
    看作一个函数对待。
    这是通过重载类的operator()来实现的

    */
#include<functional>
#if 0
//函数对象 类重载了()

template <typename T>
class ShowElemt
{
public:
    ShowElemt()
    {
        n = 0;
    }
    //重载()
    void operator()(T &t)
    {
        n++;
        cout << t << "--";
    }

    void printN()
    {
        cout << "n:" << n << endl;
    }
protected:
private:
    int n;
};

//函数模板 ==函数
template <typename T>
void FuncShowElemt(T &t)
{
    cout << t << endl;
}

//普通函数
void FuncShowElemt2(int &t)
{
    cout << t << " ";
}
//函数对象的定义:函数对象和普通函数的异同

void test01()
{
    int a = 10;
    //定义一个对象
    ShowElemt<int> ShowElemt0;
    ShowElemt0(a);//函数对象的()的执行,很像一个函数

    FuncShowElemt<int>(a);  //构造函数
    FuncShowElemt2(a);      //普通函数执行
}

/*
    函数对象是属于类对象,能突破函数的概念,能保持调用信息
    函数对象的好处
    for_each 算法中,函数对象做函数参数
    for_each 算法中,函数对象当返回值
    */
void test02()
{
    vector<int> v1;

    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);
    v1.push_back(7);

    //for_each用指定函数依次对指定范围内所有元素进行迭代访问,返回所指定的函数类型。
    //该函数不得修改序列中的元素

    //调用函数对象
    for_each(v1.begin(), v1.end(), ShowElemt<int>());//ShowElemt<int>()匿名函数对象,匿名访函数
    cout << endl;

    //调用普通函数
    for_each(v1.begin(), v1.end(), FuncShowElemt2);//通过回调函数 谁使用for_each 谁去填写回调函数的入口地址

    ShowElemt<int> show1;//定义一个有名对象
    /*
    函数: 函数名 函数参数 返回值
    template<class _InIt, class _Fn1>
    inline _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
    {   
    // perform function for each element
    _DEBUG_RANGE(_First, _Last);
    _DEBUG_POINTER(_Func);
    _For_each(_Unchecked(_First), _Unchecked(_Last), _Func);

    return (_STD move(_Func));
    }
    */
    //for_each 算法的 函数对象的传递 是元素值的传递,不是引用传递
    //用实参show1初始化形参show1,会调用这个对象的类的copy构造函数
    //实参和形参是2个不同的概念
    //形参的值可以通过for_each的返回值传递出来
    for_each(v1.begin(), v1.end(), show1);
    show1.printN();

    cout << "通过for_each算法的返回值看调用的次数" << endl;
    show1 = for_each(v1.begin(), v1.end(), show1);  //通过函数返回值,返回函数的调用状态信息
    show1.printN();
    /*
        结论要点:
        分清楚 stl算法返回值是迭代器 还是谓词(函数对象)
        是stl算法入门的重要点
        */

}

/*
    一元谓词:
    函数参数1个,函数返回值是bool类型,可以作为一个判断式
    谓词可以是一个仿函数(函数对象),也可以是一个回调函数。
    */
//template    告诉编译器要进行泛型编程
//typename T  告诉编译器看到T不要报错
template<typename T>
class IsDiv
{
public:
    IsDiv(const T &divisor)
    {
        this->divisor = divisor;
    }

    //符合一元谓词的条件
    bool operator()(T &t)
    {
        return (t%divisor == 0);
    }

protected:
private:
    T divisor;
};


void test03()
{
    vector<int> v2;
    for (int i = 10; i < 33; i++)
    {
        v2.push_back(i);
    }
    int a = 4;
    IsDiv<int> mydiv(a);

    //使用输入的函数代替等于操作符执行find
    find_if(v2.begin(), v2.end(), mydiv);//法1

    /*
        函数的返回值是一个迭代器 注意参数的类型 和返回值的类型
        template<class _InIt,class _Pr> 
        inline _InIt find_if(_InIt _First, _InIt _Last, _Pr _Pred)
        {   
        // find first satisfying _Pred
        _DEBUG_RANGE(_First, _Last);
        _DEBUG_POINTER(_Pred);
        return (_Rechecked(_First,
        _Find_if(_Unchecked(_First), _Unchecked(_Last), _Pred)));
        }
        */
    vector<int>::iterator it;
    it = find_if(v2.begin(), v2.end(), IsDiv<int>(4));//法2
    if (it == v2.end())
    {
        cout << "容器中没有被4整除的元素" << endl;
    }
    else
    {
        cout << "第一个被4整除的元素是:" << *it << endl;
    }
}

//二元函数对象
template<typename T>
class sumAdd
{
public:
    T operator()(T t1, T t2)
    {
        return t1 + t2;
    }
};

int mysum(int &t1, int &t2)
{
    return t1 + t2;
}

//二元函数对象
void test04()
{
    //v1 v2 ==> v3
    vector<int> v1, v2;
    vector<int> v3;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);

    v2.push_back(2);
    v2.push_back(4);
    v2.push_back(6);

    v3.resize(10);


    /*
    template<class _InIt1,
    class _InIt2,
    class _OutIt,
    class _Fn2> inline
    _OutIt transform(_InIt1 _First1, _InIt1 _Last1,
    _InIt2 _First2, _OutIt _Dest, _Fn2 _Func)
    {   // transform [_First1, _Last1) and [_First2, ...) with _Func
    _DEBUG_RANGE(_First1, _Last1);
    _DEBUG_POINTER(_Dest);
    _DEBUG_POINTER(_Func);
    if (_First1 != _Last1)
    return (_Transform2(_Unchecked(_First1), _Unchecked(_Last1),
    _First2, _Dest, _Func,
    _Is_checked(_Dest)));
    return (_Dest);
    }

    */
    //transform 把运算结果的 迭代器的开始位置返回出来
    transform(v1.begin(), v1.end(), v2.begin(), v3.begin(), sumAdd<int>());
    //transform(v1.begin(), v1.end(), v2.begin(), v3.begin(), mysum )
    for (vector<int>::iterator it = v3.begin(); it != v3.end(); it++)
    {
        cout << *it << " ";
    }
}

//二元谓词 函数参数2个,函数返回值是bool类型 可以作为一个判断式
//谓词可以使一个仿函数,也可以是一个回调函数
bool MyCompare(const int &a, const int &b)
{
    return a < b;//从小到大
}

void test05()
{
    vector<int> v1(10);

    for (int i = 0; i < 10; i++)
    {
        int tmp = rand() % 100;
        v1[i] = tmp;
    }

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

    //方法2 使用函数对象
    for_each(v1.begin(), v1.end(), FuncShowElemt2);
    cout << endl;

    //从小到大排序 使用二元谓词
    sort(v1.begin(), v1.end(), MyCompare);
    for_each(v1.begin(), v1.end(), FuncShowElemt2);
    cout << endl;
}

//二元谓词在set集合中的应用
struct CompareNoCase    //也可以用类
{
    bool operator()(const string &str1, const string &str2)
    {
        string str1_;
        str1_.resize(str1.size());
        transform(str1.begin(), str1.end(), str1_.begin(), tolower);//预定义函数对象

        string str2_;
        str2_.resize(str2.size());
        transform(str2.begin(), str2.end(), str2_.begin(), tolower);//预定义函数对象
        return (str1_ < str2_);
    }
};


//二元谓词在set集合中的应用
void test06()
{
    set<string> set1;
    set1.insert("bbb");
    set1.insert("aaa");
    set1.insert("ccc");
    set<string>::iterator it = set1.find("aAa");//find函数 默认 区分大小写
    if (it == set1.end())
    {
        cout << "没有查找到 aAa" << endl;
    }
    else
    {
        cout << "查找到 aAa" << endl;
    }

    set<string, CompareNoCase> set2;
    set2.insert("bbb");
    set2.insert("aaa");
    set2.insert("ccc");

    set<string, CompareNoCase>::iterator it2 = set2.find("aAa");
    if (it2 == set2.end())
    {
        cout << "没有查找到 aaa" << endl;
    }
    else
    {
        cout << "不区分大小的查找 查找到 aAa" << endl;
    }
}

void test()
{
    //test01(); //函数对象基本概念
    test02();   //函数对象的好处 函数对象做函数参数 函数对象做返回值
    //test03(); //一元谓词
    //test04(); //二元函数对象 和二元谓词
    //test05(); //二元函数对象 和二元谓词
    //test06(); //二元谓词在set集合中的应用
}


#endif

/*
    预定义函数对象:
    1)算数函数对象
    加法:plus<Types>
    减法:minus<Types>
    乘法:multiplies<Types>
    除法divides<Tpye>
    求余:modulus<Tpye>
    取反:negate<Type>
    2)关系函数对象
    等于equal_to<Tpye>
    equal_to<string> stringEqual;
    sres = stringEqual(sval1,sval2);
    不等于not_equal_to<Type>
    大于 greater<Type>
    大于等于greater_equal<Type>
    小于 less<Type>
    小于等于less_equal<Type>

    3)逻辑函数对象
    逻辑与 logical_and<Type>
    logical_and<int> indAnd;
    ires = intAnd(ival1,ival2);
    dres=BinaryFunc( logical_and<double>(),dval1,dval2);
    逻辑或logical_or<Type>
    逻辑非logical_not<Type>
    logical_not<int> IntNot;
    Ires = IntNot(ival1);
    Dres=UnaryFunc( logical_not<double>,dval1);

    函数适配器:实现将一种函数对象转化为另一种符合要求的函数对象
    plus<int> 预定义好的函数对象 能实现不同类型的数据的 +运算
    实现了 数据类型和算法的分离==>通过函数对象技术实现的
    思考 怎么样知道 plus<type> 是两个参数 查看源码
    */
#if 0
void test01()
{
    /*
    查看plus的源码
    template<class _Ty = void>
    struct plus
    : public binary_function<_Ty, _Ty, _Ty>
    {   // functor for operator+
    _Ty operator()(const _Ty& _Left, const _Ty& _Right) const
    {   // apply operator+ to operands
    return (_Left + _Right);
    }
    };
    */
    plus<int> intAdd;//预定义好的函数对象
    int x = 10;
    int y = 10;
    int z = intAdd(x, y);
    cout << "z: " << z << endl;

    plus<string> stringAdd;
    string s1 = "aaa";
    string s2 = "bbb";
    string s3 = stringAdd(s1,s2);
    cout << "s3: " << s3 << endl;

    vector<string> v1;
    v1.push_back("bbb");
    v1.push_back("aaa");
    v1.push_back("ccc");
    v1.push_back("zzz");
    v1.push_back("ccc");
    v1.push_back("ccc");
    /*
    template<class _Ty = void>
    struct greater
    : public binary_function<_Ty, _Ty, bool>
    {   // functor for operator>
    bool operator()(const _Ty& _Left, const _Ty& _Right) const
    {   // apply operator> to operands
    return (_Left > _Right);
    }
    };
    */
    sort(v1.begin(), v1.end(), greater<string>());//从大到小
    for (vector<string>::iterator it = v1.begin(); it != v1.end(); it++)
    {
        cout << *it << endl;
    }
    //求ccc出现的个数
    string sc = "ccc";
    //equal_to<string>()有两个参数 left参数来自容器 right参数来自sc
    //bind2end 函数适配器:把预定义函数对象 和第二个参数进行绑定,
    //适配成一元函数bind2nd(op, value)
    //将equal_to<string>()和 sc变为一个参数
    int num = count_if(v1.begin(), v1.end(), bind2nd(equal_to<string>(), sc));
    cout << "num : " << num << endl;

}

class IsGreat
{
public:
    IsGreat(int i)
    {
        m_num = i;
    }

    //一元谓词
    bool operator()(int &num)//num来自vector中的元素
    {
        //cout << "num" << "--" << num << "---";
        if (num > m_num)
        {
            return true;
        }
        return false;
    }
private:
    int m_num;
};
void test02()
{
    vector<int> v1;

    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i + 1);
    }
    for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
    {
        cout << *it << " ";
    }

    cout << endl;
    int num1 = count(v1.begin(), v1.end(), 3);
    cout << " num1: " << num1 << endl;

    //通过 谓词 求大于2 的个数
    int num2 = count_if(v1.begin(), v1.end(), IsGreat(2));
    cout << "num2: " << num2 << endl;

    //通过 预定义函数对象 求大于2 的个数
    //greater<int>()有两个参数 左参数来自容器的元素,右参数固定成2(通过bind2end做的)
    int num3 = count_if(v1.begin(), v1.end(), bind2nd(greater<int>(), 2));
    cout << "num3:" << num3 << endl;

    //求奇数的个数 modulus<type>求余
    int num4 = count_if(v1.begin(), v1.end(), bind2nd(modulus<int>(), 2));
    cout << "奇数的个数num4:" << num4 << endl;
    /*
    取反器(negator) : 
    negator是一个将函数对象的值翻转的函数适配器。标准库提供两个预定义
    的ngeator适配器:not1翻转一元预定义函数对象的真值,而not2翻转二元
    谓词函数的真值。
    */
    //求偶数的个数 negator取反器
    int num5 = count_if(v1.begin(), v1.end(), not1(bind2nd(modulus<int>(), 2)));
    cout << "偶数的个数 num5:" << num5 << endl;
}

void test()
{
    //test01();
    test02();
}

#endif
/*
    STL的容器算法迭代器的设计理念

    容器统一性:基于类模板技术
    实现了自定义数据类型和标准数据类型的运算
    实现了数据类型和容器模型的分离
    算法统一性:基于函数对象(本质:回调函数,将任务的编写者与任务的调用者
    进行有效的分离)

    迭代器将容器和算法整合在一起
    */

/*
    算法for_each 的使用 属于修改型算法
    */
#if 0

void printV(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << endl;
    }
}

void showElem(int &n)
{
    cout << n << " ";
}

class CMyshow
{
public:
    CMyshow()
    {
        num = 0;
    }
    //重载函数操作符
    void operator()(int &n)
    {
        num++;
        cout <<"-" << n << "-";
    }
    void printNum()
    {
        cout << "num:" << num << endl;
    }
protected:
private:
    int num;
};

void test01()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);

    printV(v1);
    cout << endl;
    /*
    template<class _InIt,
    class _Fn1> inline
    _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
    {   // perform function for each element
    _DEBUG_RANGE(_First, _Last);
    _DEBUG_POINTER(_Func);
    _For_each(_Unchecked(_First), _Unchecked(_Last), _Func);

    return (_STD move(_Func));
    }
    */
    //函数对象 回调函数的入地址
    for_each(v1.begin(), v1.end(), showElem);
    cout << endl;

    //使用匿名对象CMyshow() 实参初始化形参 会调用形参的copy构造函数
    for_each(v1.begin(), v1.end(), CMyshow());
    cout << endl;

    CMyshow mya;//用mya实参初始化形参
    //for_each()函数返回一个匿名对象,用来初始化my1  匿名对象被转正 不会被析构
    CMyshow my1 = for_each(v1.begin(), v1.end(), mya);
    cout << endl;
    mya.printNum();//mya与my1是两个不同的对象
    my1.printNum();
    cout << endl;

    my1 = for_each(v1.begin(), v1.end(), mya);  //给my1赋值 匿名对象会被析构
    cout << endl;
    my1.printNum();

}
/*
    transform的使用
    */
//transform所使用的函数对象,参数一般不使用引用,而是还有返回值
int increase(int i)
{
    return i + 100;
}

void printList(list<int> &mylist)
{
    for (list<int>::iterator it = mylist.begin(); it != mylist.end(); it++)
    {
        cout << *it << endl;
    }
}

void test02()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);

    printV(v1);
    cout << endl;

    //transform使用回调函数
    transform(v1.begin(), v1.end(), v1.begin(), increase);
    printV(v1);
    cout << endl;

    //transform使用预定义的函数对象
    transform(v1.begin(), v1.end(), v1.begin(), negate<int>());
    printV(v1);
    cout << endl;

    //transform 使用 函数适配器 和函数对象
    list<int> mylist;
    mylist.resize(v1.size());
    //转换的结果放在链表中
    transform(v1.begin(), v1.end(), mylist.begin(), bind2nd(multiplies<int>(), 10));
    printList(mylist);
    cout << endl;
    //transform 也可以把运算结果 直接输出到屏幕
    transform(v1.begin(), v1.end(), ostream_iterator<int>(cout, " "), negate<int>());
    cout << endl;
}

/*
    foreach与transform 的异同
    一般情况下:for_each所使用的函数对象,参数是引用,没有返回值
    transform所使用的函数对象,参数一般不使用引用,而是还有返回值
    */
void myshowElem(int &n)
{
    cout << n << " ";
} 

int myshowElem2(int n)
{
    cout << n << " ";
    return n;
}
void test03()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);

    vector<int> v2 = v1;
    for_each(v1.begin(), v1.end(), myshowElem);

    cout << endl;
    transform(v2.begin(), v2.end(), v2.begin(), myshowElem2);
    /*
    template<class _InIt,
    class _OutIt,
    class _Fn1> inline
    _OutIt transform(_InIt _First, _InIt _Last,
    _OutIt _Dest, _Fn1 _Func)
    {   // transform [_First, _Last) with _Func
    _DEBUG_RANGE(_First, _Last);
    _DEBUG_POINTER(_Dest);
    _DEBUG_POINTER(_Func);
    if (_First != _Last)
    return (_Transform1(_Unchecked(_First), _Unchecked(_Last),
    _Dest, _Func, _Is_checked(_Dest)));
    return (_Dest);
    }
    template<class _InIt,
    class _OutIt,
    class _Fn1> inline
    _OutIt _Transform(_InIt _First, _InIt _Last,
    _OutIt _Dest, _Fn1 _Func)
    {   // transform [_First, _Last) with _Func
    for (; _First != _Last; ++_First, ++_Dest)
    *_Dest = _Func(*_First);//解释了为什么要有返回值
    return (_Dest);
    }
    */

}

/*
    adjacent_find在iterator对标识元素范围内,查找一对相邻重复元素,
    找到则返回指向这对元素的第一个元素的迭代器。否则返回past-the-end
    */
void test04()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(5);

    vector<int>::iterator it = adjacent_find(v1.begin(), v1.end());
    if (it == v1.end())
    {
        cout << "没有找到重复的元素" << endl;
    }
    else
    {
        cout << *it << endl;
    }
    int index = distance(v1.begin(), it);//计算迭代器与元素开始的位置
    cout << "重复元素的下标为: " << index << endl;

}

/*
    binary_search二分法查找
    1000 个数使用二分法查找 1024 = 2^10次方  查10次即可 速度快
    在有序序列中查找value,找到则返回true。注意:在无序序列中,不可使用
    */
void test05()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);
    v1.push_back(7);
    v1.push_back(9);

    bool b = binary_search(v1.begin(), v1.end(), 5);
    if (b == true)
    {
        cout << "找到了" << endl;
    }
    else
    {
        cout << "没到了" << endl;
    }

}

/*
    count利用等于操作符,把标志范围内的元素与输入值比较,返回相等的个数
    */
void test06()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);
    v1.push_back(7);
    v1.push_back(7);
    v1.push_back(9);
    v1.push_back(7);

    int num = count(v1.begin(), v1.end(), 7);
    cout << num << endl;
}

/*
    使用自定义类型查找
    count_if  根据条件进行查找
    */
//一元谓词
bool GreatThree(int inum)
{
    if (inum > 3)
    {
        return true;
    }
    return false;
}

void test07()
{
    vector<int> v1;

    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);
    v1.push_back(7);
    v1.push_back(7);
    v1.push_back(9);
    v1.push_back(7);

    int num = count_if(v1.begin(), v1.end(), GreatThree);
    cout << "num:" << num << endl;
}

/*
    find 和find_if
    */
void test08()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(4);
    v1.push_back(5);
    v1.push_back(7);
    v1.push_back(7);
    v1.push_back(9);
    v1.push_back(7);

    //查找值为5 的元素的位置
    vector<int>::iterator it = find(v1.begin(), v1.end(), 5);
    cout << *it << endl;

    //返回第一个大于3 的位置
    vector<int>::iterator it2 = find_if(v1.begin(), v1.end(), GreatThree);
    cout << *it2 << endl;
}

/*
    merge:合并两个有序序列,存放到另一个序列
    */
void test09()
{
    vector<int> v1;//有序序列
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);

    vector<int> v2;//有序序列
    v2.push_back(2);
    v2.push_back(4);
    v2.push_back(6);

    vector<int> v3;
    //注意扩大容量
    v3.resize(v1.size() + v2.size());
    merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
    printV(v3);
}

/*
    sort:以默认升序的方式重新排列指定范围内的元素。
    若要改排序规则,可以输入比较函数
    */

class Student
{
public:
    Student(string name, int id)
    {
        m_name = name;
        m_id = id;
    }

    void printT()
    {
        cout << "name: " << m_name << "id: " << m_id << endl;
    }
public:
    string m_name;
    int m_id;

};

//二元谓词
bool CompareS(Student &s0, Student &s1)
{
    return (s0.m_id < s1.m_id);
}
void test10()
{
    Student s1("老大", 1);
    Student s2("老二", 2);
    Student s3("老三", 3);
    Student s4("老四", 4);

    vector<Student> v1;
    v1.push_back(s4);
    v1.push_back(s1);
    v1.push_back(s3);
    v1.push_back(s2);

    for (vector<Student>::iterator it = v1.begin(); it != v1.end(); it++)
    {
        it->printT();
    }
    /*
        根据自定义函数对象 进行自定义数据类型的排序
        替换 算法的统一性(实现算法和数据类型的分离)==》技术手段函数对象
        */
    sort(v1.begin(), v1.end(),CompareS);
    for (vector<Student>::iterator it = v1.begin(); it != v1.end(); it++)
    {
        it->printT();
    }
}

/*
   random_shuffle:对指定范围内的元素随机调整次序
srand(time(0));设置随机种子
*/
void test11()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);
    v1.push_back(7);

    random_shuffle(v1.begin(), v1.end());
    printV(v1);

    string str = "abcdefg";
    random_shuffle(str.begin(), str.end());
    cout << "str: " << str << endl;
}

/*
    reverse将所有元素取反
    */
void test12()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);
    v1.push_back(7);

    reverse(v1.begin(), v1.end());
    printV(v1);
}

/*
    常用的拷贝和替换算法
    copy拷贝算法
    */
void test13()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);
    v1.push_back(7);

    vector<int> v2;
    v2.resize(v1.size());
    copy(v1.begin(), v1.end(), v2.begin());
    printV(v2);
}

/*
    replace和replace_if

    将指定范围内的所有等于oldValue的元素替换成newValue
    replace(beg,end,oldValue,newValue)

    replace_if : 将指定范围内所有操作结果为true的元素用新值替换
    */
bool great_equal_5(int &obj)
{
    if (obj > 5)
    {
        return true;
    }
    return false;
}
void test14()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);
    v1.push_back(7);
    v1.push_back(3);

    replace(v1.begin(), v1.end(), 3, 8);
    printV(v1);

    //将容器中大于5的值用 1 替换
    replace_if(v1.begin(), v1.end(), great_equal_5, 1);
    printV(v1);
}

/*
    swap:交换两个容器的元素
    */
void test15()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);

    vector<int> v2;
    v2.push_back(2);
    v2.push_back(4);
    v2.push_back(6);

    swap(v1, v2);
    printV(v1);

}

/*
    常用的算术和生成算法
    accumulate
    对指定范围内的元素求和,然后结果再加上一个由val指定的初始值。
    fill
    将输入值赋给标志范围内的所有元素

    */
#include<numeric>
void test16()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);

    int tmp = accumulate(v1.begin(), v1.end(), 100);
    cout << "tmp: " << tmp << endl;

    fill(v1.begin(), v1.end(), 5);
    printV(v1);

}

/*
    常用的集合算法
    set_union:  
    构造一个有序序列,包含两个有序序列的并集。
    set_intersection:  
    构造一个有序序列,包含两个有序序列的交集。
    set_difference:  
    构造一个有序序列,该序列保留第一个有序序列中存在
    而第二个有序序列中不存在的元素

    */
void test17()
{
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(3);
    v1.push_back(5);

    vector<int> v2;
    v2.push_back(3);
    v2.push_back(4);
    v2.push_back(6);

    vector<int> v3;
    v3.resize(v1.size() + v2.size());
    //并集
    set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
    printV(v3);

    cout << "-----" << endl;
    vector<int> v4;
    v4.resize(v1.size() + v2.size());
    //交集
    set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), v4.begin());
    printV(v4);


    cout << "-----" << endl;
    vector<int> v5;
    v5.resize(v1.size() + v2.size());
    //差集
    set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), v5.begin());
    printV(v5);

}

void test()
{
    //test01();
    //test02();
    //test03(); //foreach与transform 的异同
    //test04(); //adjacent_find
    //test05(); //binary_search
    //test06();//count
    //test07();//count_if
    //test08();//find和find_if
    //test09();//merge
    //test10();//sort
    //test11();//random_shuffle
    //test12();//reverse
    //test13();//copy
    //test14();//replace和replace_if
    //test15();//swap
    //test16();//accumulate 和fill
    test17();
}
#endif

/*
    stl 演讲比赛
    */
#include <numeric>

#if 0
class Speaker
{
public: 
    string m_name;  //选手名
    int m_score[3]; //24个人 分为4 组 选手得分
};

//产生选手
int GenSpeaker(map<int, Speaker> &mapSpeaker, vector<int> &v)
{
    string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    random_shuffle(str.begin(), str.end()); //对指定范围内的元素随机调整次序

    for (int i = 0; i < 24; i++)
    {
        Speaker tmp;
        tmp.m_name = "选手";
        tmp.m_name = tmp.m_name + str[i];
        //选手编号 选手  key-value
        mapSpeaker.insert(pair<int, Speaker>(100 + i, tmp));
    }
    //将参加比赛的人员编号放入容器中
    for (int i = 0; i < 24; i++)
    {
        v.push_back(100 + i);
    }

    return 0;
}

//选手抽签
int speech_contest_draw(vector<int> &v)
{
    random_shuffle(v.begin(), v.end());
    return 0;
}

//选手比赛
int speech_contest(int index, vector<int> &v1, map<int, Speaker> &mapSpeaker, vector<int> &v2)
{
    //小组比赛得分 记录下来 求出前3名 后3名
    multimap<int, int, greater<int>> multimapGroup;//小组成绩

    int tmpCount = 0;

    for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
    {
        tmpCount++;

        //打分 10个评委为每一个选手打分 去掉最高分 和最低分 求剩余平均分
        {
            //选择双端队列
            deque<int> dscore;
            for (int j = 0; j < 10; j++)//10个评委打分
            {
                int score = 50 + rand() % 50;
                dscore.push_back(score);
            }
            //排序
            sort(dscore.begin(), dscore.end());//默认是升序
            dscore.pop_back();//去除最高分
            dscore.pop_front();//去除最低分
            //求平均分 则为这个选手的得分
            int scoresun = accumulate(dscore.begin(), dscore.end(), 0);
            int scoreavg = scoresun / dscore.size();
            mapSpeaker[*it].m_score[index] = scoreavg;//选手得分存入容器中
            multimapGroup.insert(pair<int, int>(scoreavg, *it));//选手得分 选手编号
        }

        //处理分组
        if (tmpCount % 6 == 0)
        {
            cout << "小组的比赛成绩" << endl;
            //得分 编号
            for (multimap<int, int, greater<int>>::iterator mit = multimapGroup.begin(); mit != multimapGroup.end(); mit++)
            {
                //编号 姓名 得分
                cout << mit->second << "\t" << mapSpeaker[mit->second].m_name << "\t" << mit->first << endl;
            }

            //前3名晋级
            while (multimapGroup.size() > 3)
            {
                multimap<int, int, greater<int>>::iterator it1 = multimapGroup.begin();
                v2.push_back(it1->second);//把前3名 放到 v2 晋级名单中
                multimapGroup.erase(it1);//迭代器自动下移
            }
            multimapGroup.clear();//用完清除
        }
    }
    return 0;
}

//查看比赛结果
int speech_contest_print(int index, vector<int> &v, map<int, Speaker> &mapSpeaker)
{
    printf("第%d 轮 晋级名单\n", index + 1);
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << "参赛编号: " << *it <<"\t" << mapSpeaker[*it].m_name << "\t" << mapSpeaker[*it].m_score[index] << endl;
    }
    return 0;
}

void test()
{
    //容器的设计
    map<int, Speaker> mapSpeaker;//参加比赛的选手
    vector<int> v1; //第1轮 演讲比赛名单
    vector<int> v2; //第2轮 演讲比赛名单
    vector<int> v3; //第3轮 演讲比赛名单
    vector<int> v4; //最后前3名演讲比赛名单

    //产生选手 得到第一轮选手的比赛名单
    GenSpeaker(mapSpeaker, v1);

    //第1 轮 选手抽签 选手比赛 查看比赛结果
    cout << "\n\n\n任意键,开始第1轮比赛" << endl;
    cin.get();
    speech_contest_draw(v1);
    speech_contest(0, v1, mapSpeaker, v2);
    speech_contest_print(0, v2, mapSpeaker);

    //第2 轮 选手抽签 选手比赛 查看比赛结果
    cout << "\n\n\n任意键,开始第2轮比赛" << endl;
    cin.get();
    speech_contest_draw(v2);
    speech_contest(1, v2, mapSpeaker, v3);
    speech_contest_print(1, v3, mapSpeaker);

    //第3 轮 选手抽签 选手比赛 查看比赛结果
    cout << "\n\n\n任意键,开始第3轮比赛" << endl;
    cin.get();
    speech_contest_draw(v3);
    speech_contest(2, v3, mapSpeaker, v4);
    speech_contest_print(2, v4, mapSpeaker);

}

#endif


int main()
{
    test();
    cout << "------";
    system("pause");
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值