(B站黑马C++)模板

本文详细介绍了C++中的函数模板和类模板的基本语法、注意事项、案例以及与普通函数的区别。讨论了模板的局限性、创建时机、作为函数参数的使用、与继承的关系,并展示了如何在类外实现成员函数以及分文件编写模板类。同时,提到了模板与友元函数的交互,最后给出了一个自定义数组类封装的模板案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

👂 总有一天你会出现在我身边 - 棱镜乐队 - 单曲 - 网易云音乐

👂 カタオモイ(单相思) - Aimer - 单曲 - 网易云音乐

目录

🌳函数模板基本语法

🌳函数模板注意事项

🌳函数模板案例 -- 数组排序

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

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

🌼模板的局限性

🌼类模板基本语法

🌼类模板与函数模板区别

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

🌼类模板对象作函数参数

🌼类模板与继承

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

🌳类模板分文件编写

第1种("person.cpp"不常用)

第2种(.hpp常用)

🌳类模板与友元

🍌类模板案例 -- 数组类封装

代码1

代码2


🌳函数模板基本语法

#include<iostream>
using namespace std;

//函数模板, 将类型参数化

//一个函数实现各种数据类型

//函数模板
template<typename T> //声明一个模板  T表示通用数据类型  不报错
void mySwap(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}

void test01()
{
    int a = 10, b = 20;

    //利用函数模板交换

    //1,自动类型推导
    mySwap(a, b); //根据传入的类型, 自动推导
    cout<<"a = "<<a<<endl<<"b = "<<b<<endl;

    //2,显示指定类型
    mySwap<int>(a, b); //指定上面<>中的T的类型

    cout<<"a = "<<a<<endl<<"b = "<<b<<endl;
}

int main()
{
    test01();

    system("pause");
    return 0;
}
//1, 函数模板  关键字  template
//2, 两种方式:自动类型推导 和 显示指定类型
//3, 提高复用性  类型参数化

🌳函数模板注意事项

#include<iostream>
using namespace std;

//函数模板注意事项

//1,自动类型推导,必须推导出一致的数据类型 T

template<typename T> //typename 等价于 class
void mySwap(T &a, T&b)
{
    T temp = a;
    a = b;
    b = temp;
}

void test01()
{
    int a = 10, b = 20;
    char c = 'c';

    //mySwap(a, b); //正确!
    //mySwap(a, c); //错误! 推导不出一致的 T 类型

    cout<<"a = "<<a<<"  b = "<<b<<endl;
}

//2,模板必须确定出 T 的数据类型
template<class T>
void func()
{
    cout<<"func 调用"<<endl;
}

void test02()
{
    //func(); //错误!error: no match function to ...
    func<int>(); //正确!
}

int main()
{
    test01();
    test02();

    system("pause");
    return 0;
}

🌳函数模板案例 -- 数组排序

#include<iostream>
using namespace std;

//实现通用 对数组排序的函数
//大到小
//选择排序
//测试 char 和 int

//交换函数模板
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) {
            if(arr[Max] < arr[j]) { //交换
                Max = j; //更新最大值下标
            }
        }
        if(Max != i) {
            //交换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 charArr[] = "badcfe";

    int num = sizeof(charArr) / sizeof(char);
    mySort(charArr, num);

    printArray(charArr, num);
}

void test02()
{
    //测试int数组
    int intArr[] = {1,8,5,2,11,8,8};

    int num = sizeof(intArr) / sizeof(int);
    mySort(intArr, num);

    printArray(intArr, num);
}

int main()
{
    test01();
    test02();

    system("pause");
    return 0;
}

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

#include<iostream>
using namespace std;

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

//1,普通函数调用时  可以隐式类型转换
//2,函数模板  自动类型推导 不能隐式类型转换
//3,函数模板  显示指定类型 可以隐式转换

//普通函数
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, b = 20;
    char c = 'c';

    //cout<<myAdd01(a, b)<<endl;
    //'c'隐式转换成整形
    cout<<myAdd01(a, c)<<endl; //c - 99, 10 + 99 == 109

    //自动类型推导
    //cout<<myAdd02(a, b)<<endl; //error, 不能隐式转换, char与int

    //显示指定类型
    cout<<myAdd02<int>(a, c)<<endl; 
}

int main()
{
    test01();

    system("pause");
    return 0;
}

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

如果提供了函数模板,就不要写普通函数了,否则容易出现 二义性

#include<iostream>
using namespace std;

//普通函数与函数模板调用规则
//1,如果函数模板和普通函数都可以调用,优先调用普通函数
//2,空模板参数列表 来强制调用  函数模板
//3,函数模板可函数重载
//4,如果函数模板可产生更好匹配,优先函数模板

void myPrint(int a, int b)
{
    cout<<"调用 普通函数"<<endl;
}

template<class T>
void myPrint(T a, T b)
{
    cout<<"调用 模板"<<endl;
}

template<class T>
void myPrint(T a, T b, T c)
{
    cout<<"调用 重载 模板"<<endl;
}

void test01()
{
    //1,
    int a = 10, b = 20;
    myPrint(a, b);

    //2,空模板参数列表  强制调用函数模板
    myPrint<>(a, b);

    //3,
    myPrint(a, b, 100);

    //4,如果函数模板产生更好的匹配 优先函数模板
    char c1 = 'a', c2 = 'b';
    myPrint(c1, c2);
}

int main()
{
    test01();

    system("pause");
    return 0;
}

🌼模板的局限性

1,具体化的模板,解决自定义类型的通用化
2,模板,是为了STL能运用系统提供的模板 

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

//模板局限性  非万能
//特定数据类型  需要具体化实现

class Person
{
public:
    Person(string name, int age)
    {
        this->m_Age = age;
        this->m_Name = name;
    }
    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, 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);

    bool ret = myCompare(p1, p2);
    if(ret)
        cout<<"a == b"<<endl;
    else
        cout<<"a != b"<<endl;
}

int main()
{
    test01();
    test02();

    system("pause");
    return 0;
}

🌼类模板基本语法

类模板与函数模板语法几乎一样

区别是,类模板声明模板template下一行加类,函数模板声明模板template下一行加函数

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

//类模板
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()
{
    //类型参数化
    //1, 将string, int传到NameType, AgeType
    //2, 将"孙悟空", 999 传到name, age
    Person<string, int> p1("孙悟空", 999);
    p1.showPerson();
}

int main()
{
    test01();

    system("pause");
    return 0;
}

🌼类模板与函数模板区别

1,类模板只能用显式指定类型的方式
2,类模板的模板参数列表可以有默认参数 

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

//类模板与函数模板区别
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: "<<m_Name<<"  age: "<<m_Age<<endl;
    }

    NameType m_Name;
    AgeType m_Age;
};

//1, 类模板没有自动类型推导的使用方式
void test01()
{
    //Person p("孙悟空", 110); //× p之前缺少模板参数
    Person<string, int>p("孙悟饭", 999); //√ 只能用显示指定类型
    p.showPerson();
}


//2,类模板在模板的参数列表中可以有默认参数
void test02()
{
    Person<string>p("猪八戒",1234); //上面类模板声明时, 有默认参数int
    p.showPerson();
}


int main()
{
    test01();
    cout<<endl;
    test02();

    system("pause");
    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()
    {
        obj.showPerson1();
    }

    void func2()
    {
        obj.showPerson2();
    }
};

void test01()
{
    MyClass<Person2>m;
    //m.func1(); //编译会出错, 说明函数调用才会去创建成员函数
    m.func2();
}

int main()
{
    test01();

    system("pause");
    return 0;
}

🌼类模板对象作函数参数

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

//类模板对象做函数参数

template<class T1, class T2>
class Person
{
public:

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

    void showPerson()
    {
        cout<<"姓名:"<<this->m_Name<<"  年龄:"<<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("孙悟空", 99);
    printPerson1(p);
}

//2,参数模板化

template<class T1, class T2>
void printPerson2(Person<T1, T2>&p)
{
    p.showPerson();
    cout<<"T1类型:"<<typeid(T1).name()<<endl; //查看模板退出来的类型
    cout<<"T2类型:"<<typeid(T2).name()<<endl;
}

void test02()
{
    Person<string, int>p("猪八戒", 55);
    printPerson2(p);
}

//3,整个类模板化

template<class T> //告知编译器下面的T是什么
void printPerson3(T &p)
{
    p.showPerson();
    cout<<"T的类型:"<<typeid(T).name()<<endl;
}

void test03()
{
    Person<string, int>p("唐僧", 23);
    printPerson3(p);
}

int main()
{
    //test01();
    //test02();
    test03();

    system("pause");
    return 0;
}
/*
总结
1, 类模板创建的对象, 3种方式向函数中传参
2, 开发中 指定传入类型 最常用
*/

🌼类模板与继承

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

//类模板与继承

template<class T>
class Base //父类
{
public:
    T m;
};

//class Son : public Base //× 需要知道父类中T的类型 子类才能继承
class Son : public Base<int> //√
{

};

void test01()
{
    Son s1;
}

//如果想灵活指定父类中 T 的类型  子类也需要变成类模板

template<class T1, class T2>
class Son2 : public Base<T2>
{
public:
    Son2()
    {
        cout<<"T1类型:"<<typeid(T1).name()<<endl;
        cout<<"T2类型:"<<typeid(T2).name()<<endl;
    }
    T1 obj;
};

void test02()
{
    Son2<int, char>S2; //int对应T1子类, char对应T2父类
}

int main()
{
    //test01();
    test02();

    system("pause");
    return 0;
}
/*
总结
如果父类时类模板  子类需要指定出父类中T的数据类型  才能继承
*/

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

#include<iostream>
using namespace std;

//类模板成员函数类外实现
template<class T1, class T2>
class Person
{
public:

    Person(T1 name, T2 age); //构造函数  类内声明
//    {
//        this->m_Name = name, this->m_Age = age;
//    }

    void showPerson(); //成员函数  类内声明
//    {
//        cout<<"姓名:"<<this->m_Name<<"  年龄:"<<m_Age<<endl;
//    }

    T1 m_Name; T2 m_Age;
};

//构造函数  类外实现

template<class T1, class T2> //让编译器知道T1 T2的存在
Person<T1, T2>::Person(T1 name, T2 age) //Person<T1, T2>是作用域, 表示类模板的类外实现
{
    this->m_Name = name, this->m_Age = age;
}

//成员函数  类外实现

template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout<<"姓名:"<<this->m_Name<<"  年龄:"<<m_Age<<endl;
}

void test01()
{
    Person<string, int>p("Tom", 19);
    p.showPerson();
}

int main()
{
    test01();

    system("pause");
    return 0;
}
/*
总结
类模板成员函数类外实现时  需要加上  模板参数列表
*/

🌳类模板分文件编写

第1种("person.cpp"不常用)

main.cpp

#include<iostream>
using namespace std;

//类模板中成员函数  一开始不会创建

//第1种解决方式  直接包含源文件.cpp

//而.h头文件  只会让编译器看见 .h里的代码  里面没有实现  只有声明
//但是 .cpp头文件 让编译器先看完.cpp的代码 再通过.cpp中的.h头文件...
#include "person.cpp"


//第2种解决方式 .h和.cpp中内容写到一起 后缀名改为 .hpp文件
//.hpp 表示存在 类模板  hpp是认为规定的名称



//类模板分文件编写的问题 和 解决方法

//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<<"姓名:"<<this->m_Name<<"  年龄:"<<m_Age<<endl;
//}

void test01()
{
    Person<string, int>p("Jerry", 13);
    p.showPerson();
}

int main()
{
    test01();

    system("pause");
    return 0;
}

person.cpp

#include "person.h"

//类外实现放.cpp
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<<"姓名:"<<this->m_Name<<"  年龄:"<<m_Age<<endl;
}

person.h

//.h中开头包含的代码
#pragma once //防止头文件重复
#include<iostream> //标准输入输出流
using namespace std; //标准命名空间

template<class T1, class T2>
class Person
{
public:

    Person(T1 name, T2 age);

    void showPerson();

    T1 m_Name; T2 m_Age;
};

第2种(.hpp常用)

main.cpp

#include<iostream>
using namespace std;

//类模板中成员函数  一开始不会创建

//第1种解决方式  直接包含源文件.cpp

//而.h头文件  只会让编译器看见 .h里的代码  里面没有实现  只有声明
//但是 .cpp头文件 让编译器先看完.cpp的代码 再通过.cpp中的.h头文件...
//#include "person.cpp"

#include "person.hpp"


//第2种解决方式 .h和.cpp中内容写到一起 后缀名改为 .hpp文件
//.hpp 表示存在 类模板  hpp是认为规定的名称



//类模板分文件编写的问题 和 解决方法

//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<<"姓名:"<<this->m_Name<<"  年龄:"<<m_Age<<endl;
//}

void test01()
{
    Person<string, int>p("Jerry", 13);
    p.showPerson();
}

int main()
{
    test01();

    system("pause");
    return 0;
}

person.hpp

person.h 和 person.cpp放一块

//.h中开头包含的代码
#pragma once //防止头文件重复
#include<iostream> //标准输入输出流
using namespace std; //标准命名空间

template<class T1, class T2>
class Person
{
public:

    Person(T1 name, T2 age); //类内声明

    void showPerson(); //类内声明

    T1 m_Name; T2 m_Age;
};

//类外实现放.cpp
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<<"姓名:"<<this->m_Name<<"  年龄:"<<m_Age<<endl;
}

总结
主流解决方法,将类模板成员函数的声明和实现写到一起,并将后缀名改为 .hpp

🌳类模板与友元

全局函数类内实现:直接类内friend声明友元
全局函数类外实现:提前让编译器知道全局函数存在 

由于类外实现较为复杂,一般类内实现,用法简单,编译器可直接识别

#include<iostream>
using namespace std;

//通过 全局函数 打印Person信息

//1,
template<class T1, class T2> //这个类是模板的
class Person; //让编译器知道有Person存在


//2,   1放2前面, 2放后面的实现前面
//类外实现
template<class T1, class T2>
void printPerson2(Person<T1, T2>p) //前面需要先让编译器知道Person存在
{
    cout<<"类外实现:   姓名:"<<p.m_Name<<"  年龄:"<<p.m_Age<<endl;
}

template<class T1, class T2>
class Person
{
public:

    //全局函数 类内实现
    friend void printPerson(Person<T1, T2> p) //参数模板化 ; 友元可以访问私有属性
    {
        cout<<"姓名:"<<p.m_Name<<"  年龄:"<<p.m_Age<<endl;
    }

    //全局函数 类外实现
    //加个空模板参数列表 <> 告诉编译器这不是普通函数 是模板函数
    //全局函数类外实现的话  需要让编译器提前知道这个函数的存在
    friend void printPerson2<>(Person<T1, T2>p);


    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", 22);

    printPerson(p);
}

//2,全局函数 类外实现
void test02()
{
    Person<string, int>p("Jerry", 33);

    printPerson2(p);
}

int main()
{
    test01();
    test02();

    return 0;
}

🍌类模板案例 -- 数组类封装

代码1

MyArray.hpp

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

template<class T>
class MyArray
{
public:

    //有参构造 参数 容量
    MyArray(int capacity)
    {
        cout<<"MyArray 有参 构造调用"<<endl;

        this->m_Capacity = capacity; //容量
        this->m_Size = 0; //大小
        this->pAddress = new T[this->m_Capacity]; //堆区数据, new一个数组
    }

    //拷贝构造  防止浅拷贝
    MyArray(const MyArray& arr)
    {
        cout<<"MyArray 拷贝 构造调用"<<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];
        }
    }

    //operator=  防止浅拷贝问题  a = b = c 等号赋值时 自身引用 以便连等
    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; //返回自身
    }


    //析构函数  手动开辟  手动释放
    ~MyArray()
    {
        cout<<"MyArray 析构 函数调用"<<endl;

        if(this->pAddress != NULL) {
            delete[] this->pAddress;
            this->pAddress = NULL; //防止野指针
        }
    }

private:
    T * pAddress; //指针指向堆区开辟的真实数组

    int m_Capacity; //数组容量

    int m_Size; //数组大小
};

测试1.cpp

#include<iostream>
using namespace std;
#include "MyArray.hpp"

void test01()
{
    MyArray <int>arr1(5); //缺<int>会报错missing template argument

    MyArray <int>arr2(arr1); //拷贝构造

    MyArray <int>arr3(100); //容量100
    arr3 = arr1; //先清空100 再拷贝arr1
}

int main()
{
    test01();

    system("pause");
    return 0;
}

输出

MyArray 有参 构造调用
MyArray 拷贝 构造调用
MyArray 有参 构造调用
MyArray operator= 调用
MyArray 析构 函数调用
MyArray 析构 函数调用
MyArray 析构 函数调用
请按任意键继续. . .

代码2

MyArray.hpp

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

template<class T>
class MyArray
{
public:

    //构造函数
    MyArray(int capacity)
    {
        this->m_Capacity = capacity; //容量
        this->m_Size = 0; //大小
        this->pAddress = new T[this->m_Capacity]; //堆区数据, new一个数组
    }

    //拷贝构造
    MyArray(const MyArray& arr)
    {
        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];
        }
        //如果 T 为对象 且包含指针 必须重载 = 操作符
        //普通类型可以直接 = , 指针类型需要深拷贝
    }

    //operator=  防止浅拷贝问题  a = b = c 等号赋值时 自身引用 以便连等
    //重载 = 操作符 防止浅拷贝问题
    MyArray& operator=(const MyArray& arr)
    {
        //先判断原来堆区是否有数据, 有就先释放
        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; //数组末尾插入value
        this->m_Size++; //更新数组大小
    }

    //尾删法
    void Pop_Back()
    {
        //让用户访问不到最后一个元素 即尾删, 逻辑上的删除
        if(this->m_Size == 0)
            return;
        this->m_Size--;
    }

    //通过下标方式访问数组中元素
    //重载 [], 以便能通过下标访问
    T& operator[] (int index) //T的引用  使函数调用可以作为左值存在
    { //比如arr[0] = 100;
        return this->pAddress[index];
    }

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

    //返回数组大小
    int getSize()
    {
        return this->m_Size;
    }



    //析构函数  手动开辟  手动释放
    ~MyArray()
    {
        if(this->pAddress != NULL) {
            delete[] this->pAddress;
            this->pAddress = NULL; //防止野指针
        }
    }

private:
    T * pAddress; //指针指向堆区开辟的真实数组

    int m_Capacity; //数组容量

    int m_Size; //数组大小
};

测试1.cpp

#include<iostream>
using namespace std;
#include "MyArray.hpp"

void printIntArray(MyArray <int>& arr)
{
   for(int i = 0; i < arr.getSize(); ++i) {
        cout<<arr[i]<<endl;
   }
}

void test01()
{
    MyArray <int>arr1(5); //缺<int>会报错missing template argument

    for(int i = 0; i < 5; ++i) {
        //尾插法  向数组中插入数据
        arr1.Push_Back(i);
    }
    cout<<"arr1的打印输出:"<<endl;

    printIntArray(arr1);

    cout<<"arr1的容量:"<<arr1.getCapacity()<<endl;
    cout<<"arr1的大小:"<<arr1.getSize()<<endl;


    MyArray <int>arr2(arr1);
    cout<<"arr2的打印输出:"<<endl;
    printIntArray(arr2);

    //尾删
    arr2.Pop_Back();
    cout<<"arr2的容量:"<<arr2.getCapacity()<<endl;
    cout<<"arr2的大小:"<<arr2.getSize()<<endl;

//    MyArray <int>arr2(arr1); //拷贝构造
//
//    MyArray <int>arr3(100); //容量100
//    arr3 = arr1; //先清空100 再拷贝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<<"姓名:"<<arr[i].m_Name<<"  年龄:"<<arr[i].m_Age<<endl;
    }
}

void test02()
{
    MyArray<Person>arr(10);
    Person p1("孙悟空",999);
    Person p2("程咬金",17);
    Person p3("妲己",45);
    Person p4("亚瑟",234);
    Person p5("小巧",89);
    Person p6("大哥",12);

    //数据插入到数组中
    arr.Push_Back(p1);
    arr.Push_Back(p2);
    arr.Push_Back(p3);
    arr.Push_Back(p4);
    arr.Push_Back(p5);
    arr.Push_Back(p6);

    //打印数组
    printPersonArray(arr);

    //输出容量
    cout<<"arr容量:"<<arr.getCapacity()<<endl;
    //输出大小
    cout<<"arr大小:"<<arr.getSize()<<endl;

}

int main()
{
    //test01();

    test02();

    system("pause");
    return 0;
}

输出

姓名:孙悟空  年龄:999
姓名:程咬金  年龄:17
姓名:妲己  年龄:45
姓名:亚瑟  年龄:234
姓名:小巧  年龄:89
姓名:大哥  年龄:12
arr容量:10
arr大小:6
请按任意键继续. . .

🥕总结

学了一些一些模板封装,真要自己手写,也只能写出小部分简单的,稍微复杂一点的肯定各种bug,没事,后续还有《Essential C++》和《算法训练营》的查漏补缺

题外话

今天群里碰到个人,自己大一408和数据库学完了,现在在读研,结果劝人不要学习,学的再好出来也大概率月入3000,这种人,不是蠢,就是坏,总之各种打击萌新,太可恶了!

一有萌新出来,就各种打击,各种看不起,可恶

“学这没用”,“学这点东西差远了”,“没机会的”,“别学了,学再多也是失业”,“现在学会了,过几年又更新换代了,放弃吧”,“使劲卷,卷到大四看到你们找不到工作哈哈”(神经病😂)

它说什么不是重点,重点是它成功打击到别人,满足了内心的欲望,借机释放自己的压力和负面情绪

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千帐灯无此声

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值