更多教程笔记请查看我的上一篇文章:点击跳转

c++自学之旅2.0(模板元编程)开始!

函数模板

//函数模板
//模板的意义是使函数可重复化
#include <iostream>
using namespace std;
//两个整型交换的函数,通过引用交换
void swapInt(int &a, int &b)
{
    int temp = a;
    a = b;
    b = temp;
}

//两个浮点型的数交换,通过引用
void swapDouble(double &a, double &b)
{
    double temp = a;
    a = b;
    b = temp;
}

//如果要交换所有的数据类型的值那么要写无数个函数
//但是用模板就可以解决这个问题,模板只有在使用的时候才会确定数据类型
//定义一个模板,T是一个通用的数据类型,可以改名字
template <typename T>

//当定义了模板之后就可以使用通用的数据类型T,T只有在函数被调用的时候才会指定数据类型
void mySwap(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}

void test01()
{
    int a = 10, b = 20;
    // swapInt(a, b);
    //利用函数模板来交换两个整型的值
    //模板有两种使用方式
    // 1.自动类型推导,也就是直接使用,T自动推导出来传入的数据类型
    // mySwap(a, b);

    // 2.显示指定类型,也就是直接告诉模板传入的是什么类型
    //也就是说<>括号里面的就是告诉模板的类型,下面的函数就是告诉模板要传int类型的参数进去
    mySwap<int>(a, b);

    cout << "a=" << a << " "
         << "b=" << b << endl;
    double c = 1.1, d = 2.2;
    swapDouble(c, d);
    cout << "c=" << c << " "
         << "d=" << d << endl;
}
int main()
{
    test01();
    return 0;
}

函数模板的注意事项

//函数模板的注意事项
#include <iostream>
using namespace std;
//函数模板可以使用class替代typename,也可以继续用typename和后面的类模板做区分
template <class T>
void mySwap(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}
void test01()
{
    int a = 10;
    int b = 20;
    char c = 'c';
    //使用模板
    // mySwap(a, b); 正确
    //错误,因为模板类型不一致,无法匹配
    // mySwap(a, c);
    cout << "a=" << a << " "
         << "b=" << b << endl;
}

template <class T>
void func()
{
    cout << "func" << endl;
}
void test02()
{
    //错误,函数模板里面必须要用到T才能使用
    // func();
    //解决方法:直接确定T的数据类型
    func<int>();
}
int main()
{
    // 1.自动类型推导,必须推导出一致的数据类型,T才可以使用
    test01();
    // 2.模板必须要确定出T的数据类型才可以使用
    test02();
    return 0;
}

函数模板案例-数组排序

//函数模板案例-数组排序
#include <iostream>
using namespace std;

//交换模板实现
template <class T>
void mySwap(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}

//排序算法模板实现
template <class T>
void mySort(T arr[], int len) //第一个参数是数组,第二个参数是数组长度
{
    //选择排序算法
    for (int i = 0; i < len; i++)
    {
        //认定最大值的下标
        int max = i;
        for (int j = i + 1; j < len; j++)
        {
            //如果认定的最大值比遍历出的数值要小,说明j下标的元素才是真正的最大值
            if (arr[max] < arr[j])
            {
                max = j;
            }
        }
        //如果排序出来后max不再是一开始认定的值了那就交换两边的最大值
        if (max != i)
        {
            mySwap(arr[max], arr[i]);
        }
    }
}

//提供输出的模板
template <class T>
void printArray(T arr[], int len)
{
    for (int i = 0; i < len; i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}

void test01()
{
    //测试char数组
    char charArray[] = "badcfe";
    int num = sizeof(charArray) / sizeof(charArray[0]);
    //排序
    mySort(charArray, num);
    //输出
    printArray(charArray, num);
}
void test02()
{
    //测试int数组
    int intArray[] = {7, 5, 1, 3, 9, 2, 4, 6, 8};
    int num = sizeof(intArray) / sizeof(int);
    //排序
    mySort(intArray, num);
    //输出
    printArray(intArray, num);
}
int main()
{
    // test01();
    test02();
    return 0;
}

普通函数与模板函数的区别

//普通函数与模板函数的区别
#include <iostream>
using namespace std;
//普通函数
int myAdd01(int a, int b)
{
    return a + b;
}

//函数模板
template <class T>
T myAdd02(T a, T b)
{
    return a + b;
}

void test01()
{
    int a = 10;
    int b = 20;
    char c = 'c';
    //普通函数调用,隐式转换,会将c字符转换为ASCII码中的99再相加
    cout << myAdd01(a, c) << endl;
    //自动类型推导,使用模板调用
    cout << myAdd02(a, b) << endl;
    //错误,无法推导出一致的类型
    // myAdd02(a, c);
    //解决方法,显示指定类型调用,指定为int,会发生隐式类型转换
    cout << myAdd02<int>(a, c) << endl;
}
int main()
{
    // 1.普通函数调用可以发生隐式类型转换
    test01();
    // 2.函数模板 用自动类型推导,不可以发生隐式类型转换
    // 3.函数模板 用显示指定类型,可以发生隐式类型转换
    return 0;
}

普通函数与函数模板的调用规则

//普通函数与函数模板的调用规则
#include <iostream>
using namespace std;
//普通函数
void myPrint(int a, int b)
{
    cout << "my function" << endl;
}

//利用模板进行函数重载
//这是可以重载的,因为函数类型不一样
template <class T>
void myPrint(T a, T b)
{
    cout << "my template function" << endl;
}

//函数模板重载函数模板
template <class T>
void myPrint(T a, T b, T c)
{
    cout << "my template2 functions" << endl;
}

void test01()
{
    int a = 10;
    int b = 20;
    //当普通函数和函数模板发生重载(重名)时优先调用普通函数
    // myPrint(a, b);

    //通过空模板参数列表,强制调用函数模板
    //所谓空模板也就是什么类型都不传,直接传参数
    // myPrint<>(a, b);

    //函数模板重载函数模板,通过参数不同来区分
    // myPrint(a, b, 100);

    //如果函数模板产生更好的匹配,优先调用函数模板
    char c1 = 'a';
    char c2 = 'b';
    //优先调用了模板,因为普通函数会发生隐式转换,而模板可以直接匹配
    myPrint(c1, c2);
}

int main()
{
    //注意:1.普通函数与函数模板是可以发生函数重载的
    // 2.可以使用空模板参数列表来强制调用函数模板
    // 3.如果函数模板和普通函数都可以实现,则优先调用普通函数
    // 4.如果函数模板可以产生更好的匹配,则优先调用函数模板
    test01();
    return 0;
}

模板的局限性

//模板的局限性
#include <iostream>
using namespace std;
#include <string>

//定义一个类(自定义数据类型)
class Person
{
public:
    Person(string name, int age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }

    string m_Name;
    int m_Age;
};

//利用模板实现对比两个数据是否相等的函数
template <class T>
bool myCompare(T &a, T &b)
{
    if (a == b)
    {
        return true;
    }
    else
    {
        return false;
    }
}

//自定义一个Person的函数模板进行对象之间的判断操作
//这样就不会与上面的函数模板发生冲突
//这个相当于在原来的模板扩充功能,功能增加了
template <>
bool myCompare(Person &p1, Person &p2)
{
    if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age)
    {
        return true;
    }
    else
    {
        return false;
    }
}

void test01()
{
    int a = 10;
    int b = 20;
    //判断两个数据是否相等
    bool ret = myCompare(a, b);
    if (ret)
    {
        cout << "a==b" << endl;
    }
    else
    {
        cout << "a!=b" << endl;
    }
}

void test02()
{
    Person p1("Tom", 10);
    Person p2("Tom", 10);
    //错误,这就是模板的局限性,无法识别用自定义的数据类型进行==操作
    //第一种方法:可以在函数模板里面对==符号进行运算符重载
    //第二种方法:可以再自定义一个Person类型的函数模板,让它优先走这个函数模板
    bool ret = myCompare(p1, p2);
    if (ret)
    {
        cout << "p1==p2" << endl;
    }
    else
    {
        cout << "p1!=p2" << endl;
    }
}

int main()
{
    // test01();
    test02();
    return 0;
}

类模板

//类模板
#include <iostream>
using namespace std;
#include <string>

//建立一个通用类,类中的成员的数据类型是不确定的,只有在调用时才确定具体的类型
//第一个NameType代表名字的类型,第二个AgeType代表年龄的类型
//也就是说模板中可以定义两个不同的类型
template <class NameType, class AgeType>
class Person
{
public:
    //构造函数
    Person(NameType name, AgeType age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    //输出函数
    void showPerson()
    {
        cout << "name:" << this->m_Name << " age:" << this->m_Age << endl;
    }

    NameType m_Name;
    AgeType m_Age;
};
void test01()
{
    //通过模板创建对象,传入不同的类型给构造函数初始化属性
    Person<string, int> p1("swk", 999);
    //输出
    p1.showPerson();
}
int main()
{
    test01();
    return 0;
}

类模板与函数模板的区别

//类模板与函数模板的区别
#include <iostream>
using namespace std;
#include <string>

//定义一个类模板
// template <class NameType, class AgeType>
//第二种定义类模板的方式,指定数据类型,这样在调用模板类的时候只需要传入一个类型就行了
//另外一个类型已经在定义模板时定义好了
template <class NameType, class AgeType = int>
class Person
{
public:
    Person(NameType name, AgeType age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }

    //输出函数
    void showPerson()
    {
        cout << "name: " << this->m_Name << " age = " << this->m_Age << endl;
    }
    NameType m_Name;
    AgeType m_Age;
};
// 1.类模板没有自动类型推导的使用发生
void test01()
{
    //错误,无法用自动类型推导的方式调用构造函数传值,只能用模板(显示指定类型)的方式
    // Person p("swk", 1000);
    //正确
    Person<string, int> p("swk", 1000);
    //输出
    p.showPerson();
}

void test02()
{
    //因为在定义模板的时候已经指定了一个数据类型,所以在调用的时候只需要传入另一个类型就行了
    Person<string> p("zbj", 999);
    //输出
    p.showPerson();

    //注:如果默认参数传的是 char ,而你显示指定 int ,编译器仍可以进行隐式转换
}

// 2.类模板在参数列表中可以有默认参数

int main()
{
    // test01();
    test02();
    return 0;
}

类模板中成员函数的创建时机

//类模板中成员函数的创建时机
#include <iostream>
using namespace std;
class Person1
{
public:
    //成员函数
    void showPerson1()
    {
        cout << "Person1 show" << endl;
    }
};
class Person2
{
public:
    //成员函数
    void showPerson2()
    {
        cout << "Person2 show" << endl;
    }
};
//类模板
template <class T>
class MyClass
{
public:
    T obj;
    //类模板中的成员函数
    void func1()
    {
        // T如果是上面两个类的类型就可以点出来他们的函数
        obj.showPerson1();
    }
    //类模板中的成员函数,在一开始并不创建,而是再模板调用时再生成
    void func2()
    {
        obj.showPerson2();
    }
};
void test01()
{
    //这算是一个类成员函数调用另一个类的成员函数
    MyClass<Person1> m;
    m.func1();
    MyClass<Person2> m2;
    m2.func2();
}
int main()
{
    test01();
    return 0;
}

类模板对象做函数参数

//类模板对象做函数参数
#include <iostream>
using namespace std;
#include <string>

// T1代表string类型,T2代表int类型
template <class T1, class T2>
class Person
{
public:
    Person(T1 name, T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    void showPerson()
    {
        cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;
    }
    T1 m_Name;
    T2 m_Age;
};

// 1.指定传入类型,也就是定义什么就传什么给函数作为形参,通过引用的方式来接收
void printPerson1(Person<string, int> &p)
{
    p.showPerson();
}
void test01()
{
    Person<string, int> p("swk", 100);
    printPerson1(p);
}
// 2.参数模板化,传递,将模板中定义好的类型作为参数的类型
template <class T1, class T2>
void printPerson2(Person<T1, T2> &p)
{
    p.showPerson();
    //通过typeid().name()可以看到函数自动推导出来的数据类型
    cout << "T1 :" << typeid(T1).name() << endl;
    cout << "T2 :" << typeid(T2).name() << endl;
}
void test02()
{
    Person<string, int> p("zbj", 90);
    printPerson2(p);
}
// 3.整个类模板化,也就是直接把Person定义为T,当传入参数时就自动推导出T是Person类型
template <class T>
void printPerson3(T &p)
{
    p.showPerson();
    //查看T的数据类型
    cout << "T: " << typeid(T).name() << endl;
}
void test03()
{
    Person<string, int> p("tanshen", 30);
    printPerson3(p);
}

int main()
{
    // test01();
    // test02();
    test03();
    return 0;
}

类模板与继承

//类模板与继承
#include <iostream>
using namespace std;
//通过类模板创建一个父类
template <class T>
class Base
{
public:
    T m;
};
//子类继承模板父类
//错误,必须要知道父类中的T类型,才能继承给子类
// class Son : public Base
//正确,直接告诉父类中的T是int类型,才可以继承
class Son : public Base<int>
{
public:
};
void test01()
{
    Son s1;
}

//如果想灵活的指定父类中的T类型,那么子类也需要变为模板类
//其实也就是在继承时把父类原本的模板类型T,抽象成下面的T2,T2就代表父类的T,T1就代表子类自己用的模板类型
//当函数调用时会把T2所接收的数据类型传给父类的T,让父类的T也等于这个类型
//看到孩子眼睛是黑色的,那就知道父亲眼睛也是黑色,同理
template <class T1, class T2>
class Son2 : public Base<T2>
{
public:
    Son2()
    {
        cout << "T1: " << typeid(T1).name() << endl
             << "T2: " << typeid(T2).name() << endl;
    }
    T1 obj;
};
void test02()
{
    //通过模板创建一个子类的对象,其中会把char类型传给T2,而T2又代表父类的继承模板T
    //所以也会把char类型传给父类的T
    Son2<int, char> S2;
}
int main()
{
    // test01();
    test02();
    return 0;
}

类模板成员函数的类外实现

//类模板成员函数的类外实现
#include <iostream>
using namespace std;
#include <string>
template <class T1, class T2>
class Person
{
public:
    //只留函数声明,函数的具体实现通过类外实现
    Person(T1 name, T2 age);
    // {
    //     this->m_Name = name;
    //     this->m_Age = age;
    // }
    void showPerson();
    // {
    //     cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;
    // }
    T1 m_Name;
    T2 m_Age;
};

//模板构造函数类外实现,要在函数声明时加上template和在::作用域的前面加上<>符号
template <class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->m_Name = name;
    this->m_Age = age;
}

//模板成员函数的类外实现
template <class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;
}

void test01()
{
    //通过类外实现模板构造函数的方式来创建对象
    Person<string, int> P("Tom", 20);
    P.showPerson();
}
int main()
{
    test01();
    return 0;
}

类模板类外实现的分文件编写

day13.cpp文件内容:

//类模板类外实现的分文件编写
#include <iostream>
using namespace std;
//包含.cpp头文件的原因:
//因为类模板的成员函数在调用时才会创建,所以编译器会先看到.h的文件内容
//但是编译器在没有看到.cpp文件的内容之前是无法识别.h的文件内容的,所以直接引用.h会报错
//#include "day13_person.cpp"

//第二种解决方法:将.h和.cpp文件中的内容写到一起,将后缀改为.hpp文件
#include "day13_person.hpp"

void test01()
{
    Person<string, int> p("Jerry", 18);
    p.showPerson();
}
int main()
{
    test01();
    return 0;
}

day13_person.hpp文件内容:

//hpp = h + cpp,
//此文件的作用是把.h和.cpp分文件中的内容全部写到一起
//解决.h文件引入时模板冲突的问题
//防止头文件重复包含
#pragma once
#include <iostream>
using namespace std;
#include <string>

template <class T1, class T2>
class Person
{
public:
    //模板函数的声明
    Person(T1 name, T2 age);
    void showPerson();
    T1 m_Name;
    T2 m_Age;
};
//模板构造函数的类外分文件实现
template <class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->m_Name = name;
    this->m_Age = age;
}
//模板成员函数的类外分文件实现
template <class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;
}

模板类与友元

//模板类与友元
#include <iostream>
using namespace std;
#include <string>
//通过全局函数,打印Person信息

//因为全局函数类外实现比较麻烦
//要先声明模板类让编译器知道有这个类存在
template <class T1, class T2>
class Person;

//再把全局函数的具体实现放到最上面让编译器看到
//类外实现全局函数,因为要在类外识别类内的T1和T2,所以要加template
template <class T1, class T2>
void printPerson2(Person<T1, T2> p)
{
    cout << "name wai: " << p.m_Name << " age wai: " << p.m_Age << endl;
}
//最后才在类里面声明全局函数

//定义一个模板类
template <class T1, class T2>
class Person
{
    //全局函数,类内实现
    //这个函数是全局函数,不是成员函数,他只是被类内调用了
    //因为是私有想要类外调用  所以要加友元 加了友元 编译器才不管你类内类外 你就是全局函数
    friend void printPerson(Person<T1, T2> p)
    {
        cout << "name: " << p.m_Name << " age: " << p.m_Age << endl;
    }

    //全局函数,类外实现,类内声明
    //加一个空模板的参数列表
    //如果全局函数是类外实现,需要让编译器提前知道这个函数的存在
    friend void printPerson2<>(Person<T1, T2> p);

public:
    Person(T1 name, T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }

private:
    T1 m_Name;
    T2 m_Age;
};

void test01()
{
    //调用类内全局函数
    Person<string, int> p("Tom", 20);
    printPerson(p);
}

void test02()
{
    //调用类外全局函数
    Person<string, int> p("Jorry", 20);
    printPerson2(p);
}
int main()
{
    // test01();
    test02();
    return 0;
}

类模板案例-实现一个通用的数组类(分文件编写)

day15.cpp文件内容:

//类模板案例-实现一个通用的数组类
#include <iostream>
using namespace std;
#include "day15_MyArray.hpp"
//输出数组
void printIntArray(MyArray<int> &arr)
{
    for (int i = 0; i < arr.getSize(); i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}
//测试数组
void test01()
{
    //创建一个模板对象,调用有参构造赋值为5
    MyArray<int> arr1(5);

    for (int i = 0; i < 5; i++)
    {
        //利用尾插法向数组中插入数据
        arr1.Push_Back(i);
    }

    cout << "arr1: " << endl;
    printIntArray(arr1);

    //输出数组的容量
    cout << "arr1 area:" << arr1.getCapacity() << endl;

    //输出数组的长度
    cout << "arr1 size:" << arr1.getSize() << endl;

    //测试类中的拷贝构造函数
    MyArray<int> arr2(arr1);
    cout << "arr2: " << endl;
    printIntArray(arr2);
    //尾删
    arr2.Pop_Back();

    //输出尾删后的数组的容量
    cout << "arr2 area:" << arr2.getCapacity() << endl;

    //输出尾删后的数组的长度
    cout << "arr2 size:" << arr2.getSize() << endl;

    //测试函数重载,一开始arr3的容量是100
    //后来等号赋值的时候会先将arr3中的数据清空
    //然后再把arr1的数据赋给arr3
    // MyArray<int> arr3(100);
    // arr3 = arr1;
}

//测试自定义的数据类型
class Person
{
public:
    Person(){};
    Person(string name, int age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    string m_Name;
    int m_Age;
};
//输出对象
void printPersonArray(MyArray<Person> &arr)
{
    for (int i = 0; i < arr.getSize(); i++)
    {
        cout << "name: " << arr[i].m_Name << " age: " << arr[i].m_Age << endl;
    }
}
//测试自定义的数据类型
void test02()
{
    //传入自定义的数据类型
    MyArray<Person> arr(10);
    Person p1("swk", 999);
    Person p2("zbj", 30);
    Person p3("zs", 20);
    Person p4("lisi", 25);
    Person p5("wangwu", 27);
    //将数据插入到数组中
    arr.Push_Back(p1);
    arr.Push_Back(p2);
    arr.Push_Back(p3);
    arr.Push_Back(p4);
    arr.Push_Back(p5);

    //打印数组
    printPersonArray(arr);

    //输出容量
    cout << "arr area: " << arr.getCapacity() << endl;
    //输出长度
    cout << "arr size: " << arr.getSize() << endl;
}

int main()
{
    // test01();
    test02();
    return 0;
}

day15_MyArray.hpp文件内容:

//写一个自己的通用的数组类
#pragma once
#include <iostream>
#include <string>
using namespace std;

template <class T>
class MyArray
{
public:
    //有参构造 参数 容量
    MyArray(int capacity)
    {
        // cout << "Myarray open" << endl;
        this->m_Capacity = capacity;
        this->m_Size = 0;
        // new一个相等于数组容量的空间
        this->pAddress = new T[this->m_Capacity];
    }

    //拷贝构造函数,防止浅拷贝的问题
    MyArray(const MyArray &arr)
    {
        // cout << "Myarray copy" << endl;
        this->m_Capacity = arr.m_Capacity;
        this->m_Size = arr.m_Size;
        // this->pAddress = arr.pAddress;
        //深拷贝,重新在堆区创建一个空间
        this->pAddress = new T[arr.m_Capacity];
        //将arr中的数据都拷贝过来
        for (int i = 0; i < this->m_Size; i++)
        {
            this->pAddress[i] = arr.pAddress[i];
        }
    }
    //重载等于符号,防止浅拷贝的问题
    //返回一个引用来实现链式编程
    MyArray &operator=(const MyArray &arr)
    {
        // cout << "Myarray operator=" << endl;
        //先判断原来堆区是否有数据,如果有先释放
        if (this->pAddress != NULL)
        {
            delete[] this->pAddress;
            this->pAddress = NULL;
            this->m_Capacity = 0;
            this->m_Size = 0;
        }
        //拷贝
        this->m_Capacity = arr.m_Capacity;
        this->m_Size = arr.m_Size;
        //深拷贝
        this->pAddress = new T[arr.m_Capacity];
        //把数组中的数据都拿过来
        for (int i = 0; i < this->m_Size; i++)
        {
            this->pAddress[i] = arr.pAddress[i];
        }
        //将自身返回
        return *this;
    }

    //尾插法
    void Push_Back(const T &val)
    {
        //判断容量是否等于大小
        if (this->m_Capacity == this->m_Size)
        {
            //容量已满,无法插入
            return;
        }
        //将数据插入到数组当中的最后一个位置
        this->pAddress[this->m_Size] = val;
        //更新数组的大小
        this->m_Size++;
    }

    //尾删法
    void Pop_Back()
    {
        //让用户访问不到最后一个元素,即为尾删
        if (this->m_Size == 0)
        {
            //如果数组里没有数据就不删,直接退出
            return;
        }
        //数组的大小减一,则用户就访问不到最后一个下标了
        this->m_Size--;
    }

    //通过下标的方式访问数组中的元素,也就是重载[]符号
    //如果函数调用后还想作为左值存在则需要返回引用 arr[0] = 100;
    //把数据本身返回,再进行赋值的操作
    T &operator[](int index)
    {
        //返回数组中下标对应的数据
        return this->pAddress[index];
    }

    //返回数组的容量
    int getCapacity()
    {
        return this->m_Capacity;
    }

    //返回数组有效数据的长度
    int getSize()
    {
        return this->m_Size;
    }
    //析构函数
    ~MyArray()
    {
        // cout << "Myarray end" << endl;
        if (this->pAddress != NULL)
        {
            //这里是对象数组而不是对象指针数组
            //所以不需要写for循环去释放每一个数组元素
            delete[] this->pAddress;
            this->pAddress = NULL;
        }
    }

private:
    //指针指向堆区开辟的真实数组
    T *pAddress;
    //记录数组的容量
    int m_Capacity;
    //数组的元素个数(大小)
    int m_Size;
};