1.2、类模板

1.2、类模板

1.2.1、类模板语法

#include <iostream>
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()
{
    Person<string , int> p1("小红" , 33);
    p1.showPerson();
}

int main() {
    test01();


    return 0;
}

1.2.2、类模板与函数模板的区别

  • 类模板使用只能用显示指定类型方式
  • 类模板中的模板参数列表可以有默认参数
#include <iostream>
using namespace std;


//类模板与函数模板区别
template<class NameType , class AgeType = int>
class Person
{
public:
    Person(NameType name , AgeType age)
    {
        this->m_Age = age;
        this->m_Name = name;
    }

    void show()
    {
        cout << "name : " << this->m_Name << " age :" << this->m_Age << endl;
    }

    NameType m_Name;
    AgeType m_Age;
};

//1、类模板没有自动类型推导使用方式
void test01()
{
    //Person p ("小红",111); //错误,无法自动类型推导

    //正确
    Person<string,int> p("小红",111);
    p.show();
}
//2、类模板在模板参数列表中可以有默认参数
void test02()
{
    Person<string> p ("猪",22);
    p.show();
}

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

    return 0;
}

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

  • 类模板中成员函数在调用时才去创建

类模板中的成员函数并不是一开始就创建的,在调用时才创建

#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 test()
{
    MyClass<Person2> m;
   // m.func1();
    m.func2();
}

int main() {
    test();


    return 0;
}

1.2.4、类模板对象做函数参数

三种传入方式:

  • 指定传入的类型 (最常用)
  • 参数模板化
  • 整个类模板化
#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 show()
    {
        cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
    }
    T1 m_Name;
    T2 m_Age;
};

//1、指定传入类型
void printPerson1(Person<string,int> &p)
{
    p.show();
}
void test01()
{
    Person<string,int> p("小猪",22);
    printPerson1(p);
}

//2、参数模板化
template<class T1 , class T2>
void printPerson2(Person<T1 , T2> &p)
{
    p.show();
    cout << "T1 的类型为:" << typeid(T1).name() << endl;
    cout << "T2 的类型为:" << typeid(T2).name() << endl;
}
void test02()
{
    Person<string,int> p("柚子",22);
    printPerson2(p);
}

//3、整个类模板化
template<class T>
void printPerson3(T &p)
{
    p.show();
    cout << "T 的类型为:" << typeid(p).name() << endl;
}
void test03()
{
    Person<string,int> p("柚子",22);
    printPerson3(p);
}
int main() {

    //test01();
    //test02();
    test03();
    return 0;
}

1.2.5、类模板与继承

  • 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
  • 如果不指定,编译器无法给子类分配内存
  • 如果想灵活指定出父类中T类型,子类也需要变为类模板
#include <iostream>
using namespace std;

//类模板与继承

template<class T>
class Base
{
   
    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 main() {

   // test01();

    test02();
    return 0;
}

1.2.6、类模板成员函数类外实现

#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 << " 年龄:" << this->m_Age << endl;
    }*/

    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 << " 年龄:" << this->m_Age << endl;
}
void test01()
{
    Person<string,int> p("小猪",22);
    p.showPerson();
}
int main() {
    test01();


    return 0;
}

1.2.7、类模板分文件编写

问题:

  • 类模板中成员函数创建时机是在调用阶段,导致文件编写时链接不到

解决:

  • 方法一:直接包含.cpp源文件

  • 方法二:将声明和实现写到同一个文件中,并更改后缀为 .hpp是约定的名称,并不是强制

person.hpp中代码

#include <iostream>

using namespace std;

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

    Person(T1 name, T2 age);

    void showPerons();

    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>::showPerons()
{
    cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}


person.h中代码

//
// Created by Administrator on 2021/8/6.
//

#ifndef _03_STL_PERSON_H
#define _03_STL_PERSON_H

#include <iostream>

using namespace std;

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

    Person(T1 name, T2 age);

    void showPerons();

    T1 m_Name;
    T2 m_Age;
};


#endif //_03_STL_PERSON_H

person.cpp中代码

//
// Created by Administrator on 2021/8/6.
//
#include "person.h"
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>::showPerons()
{
    cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}

测试

#include <iostream>
using namespace std;

//第一种解决方法,直接包含 源文件
//#include "person.cpp"

//第二种解决方法,将.h和 .cpp的内容放到一起,将后缀名改为 .hpp (常用)
#include "Person.hpp"


//类模板分文件编写问题以及解决
/*

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

    Person(T1 name, T2 age);

    void showPerons();

    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>::showPerons()
{
    cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}
*/


void test01()
{
    Person<string,int> p ("小猪" ,22);
    p.showPerons();
}


int main() {

    test01();

    return 0;
}

1.2.8、类模板与友元

  • 全局函数类实现 — 直接在类内声明友元就可以了
  • 全局函数类外实现 – 需要提前让编译器知道全局函数的存在
#include <iostream>
using namespace std;

//提前让编译器知道Person类存在
template<class T1 ,class T2>
class Person;

//全局函数 类外实现
template<class T1 , class T2>
void printPerson2(Person<T1,T2> p)
{
    cout << "类外实现 ------------姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
}


//通过全局函数 打印Person信息
template<class T1 , class T2>
class Person
{
    //全局函数 类内实现
    friend void printPerson(Person<T1,T2> p)
    {
        cout << "姓名:" << p.m_Name << " 年龄:" << 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 ("猪",22);
    printPerson(p);
}

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

int main() {

    //test01();
    test02();
    return 0;
}

1.2.9、类模板案例

实现一个通用的数组类,要求如下 :

  • 可以对内置数据类型以及自定义数据类型的数据进行存储
  • 将数组中的数据存储到堆区
  • 构造函数可以传入数组的容量
  • 提供对应的拷贝构造函数以及operator=防止线拷贝问题
  • 提供尾插法和尾删除法对数组中的数据进行增加和删除
  • 可以通过下标的方式访问数组中的元素
  • 可以获取数组中当前元素个数和数组的容量

MyArray.hpp

#ifndef _03_STL_MYARRAY_H
#define _03_STL_MYARRAY_H

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

template<class T>
class MyArray
{
public:

    //有参构造  参数 容量
    MyArray(int capacity)
    {
       // cout << "有参构造函数" << endl;
        this->m_Capacity = capacity;
        this->m_Size = 0;
        this->pAddress = new T[this->m_Capacity];
    }

    //拷贝构造
    MyArray(const MyArray &arr)
    {
        //cout << "拷贝构造函数" << 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= 防止线拷贝问题
    MyArray& operator=(const MyArray &arr)
    {
        //cout << "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_Capacity ; 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 << "析构函数调用" << endl;
        if(this->pAddress != NULL)
        {
            delete[] this->pAddress;
            this->pAddress;
        }
    }
private:
    T * pAddress; //指针指向堆区开辟的真实数组

    int m_Capacity; //数组容量

    int m_Size; //数组大小
};

#endif //_03_STL_MYARRAY_H

  • 测试代码
#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);

    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尾删除后" << endl;
    cout << "arr2的容量为: " << arr2.getCapacity() << endl;
    cout << "arr2的大小为: " << arr2.getSize() << endl;


   /* 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 printPerosnArray(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("小a",22);
    Person p2("小b",2212);
    Person p3("小c",232);
    Person p4("小d",224);
    Person p5("小e",212);

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

    printPerosnArray(arr);
}
int main() {
   // test01();
    test02();

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值