C++模板

1.函数模板

函数模板的作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表

语法

template<typename T>
函数的声明或定义

template --- 声明创建模板

typename --- 表面其背后的符号是一种数据类型,可以用class代替

T --- 通用的数据类型,名称可以替换,通常为大写字母

#include <iostream>
using  namespace std;

template<typename T>
void myswap(T &a,T &b)
{
    T temp = a;
    a = b;
    b= temp;
}

void test()
{
    int a = 10;
    int b = 20;
    myswap<int>(a,b);
    cout << "a=" << a << endl;
    cout << "b=" << b << endl;
}

int main()
{
    test();
    return 0;
}

函数模板案例

案例描述:

利用函数封装一个排序的数组,可以对不同数据类型数组进行排序

排序规则从小到大,排序算法为选择排序

#include <iostream>
using  namespace std;


template<typename T>
void myswap(T &a,T &b)
{
    T temp=a;
    a = b;
    b= temp;
}

template<typename T>
void sort(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 != arr[i])
        {
            myswap(arr[i],arr[max]);
        }
    }
}


int main()
{
    char s[]="fecba";;
    sort(s,5);

    cout << s;

    return 0;
}

模板的局限性

模板的通用性并不是万能的

template<class T>
void f(T a,T b)
{
    if(a > b){ ... }
}

在上述代码中,如果传入的是数组或者类,就无法实现了

2.类模板

类模板的作用:

建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表

语法:

template<typename T>
类

实例:

#include <iostream>
using  namespace std;


template<class NameType,class AgeType>
class Person
{
public:
    Person(NameType name,AgeType age)
    {
        this->name=name;
        this->age=age;
    }
    NameType name;
    AgeType age;

    void showPerson()
    {
        cout << "name:" << name << endl;
        cout << "age:" << age << endl;
    }
};

void test()
{
    Person<string,int> p1("小明",28);
    p1.showPerson();
}

int main()
{
    test();

    return 0;
}

类模版和函数模板区别:
1.类模版没有自动类型推导使用方式
template<class NameType,class AgeType = int>
class Person
{
public:
    Person(NameType name,AgeType age)
    {
        this->name=name;
        this->age=age;
    }
    NameType name;
    AgeType age;

    void showPerson()
    {
        cout << "name:" << name << endl;
        cout << "age:" << age << endl;
    }
};

void test()
{
    Person p1("小明",28)//错误,类模版使用的时候不可以用自动类型推导
    Person<string,int> p1("小明",28);//必须使用指定类型的方式,使用类模版
    p1.showPerson();
}

int main()
{
    test();

    return 0;
}
2.类模版在模板参数列表中可以有默认参数
template<class NameType,class AgeType = int>
类模版与继承
template<class T>
class Base
{
    T m;
};

//class Son : public Base 这样继承是错误的,必须知道父类中的T类型,才能继承给子类
class Son : public Base<int>
{

};

类模板成员函数类外实现

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

//构造函数类外实现
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:" << m_Name << endl;
    cout << "age:" << m_Age << endl;
}

int main() {
    Person<string,int>p1("小明",22);
    p1.showPerson();

    return 0;
}
类模版分文件编写

问题:

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

解决:

        解决方式1:直接包含.cpp文件

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

类模版与友元

全局函数类内实现 - 直接在类内声明友元即可

全局函数类外实现 - 需要提前要编译器知道全局函数的存在

#include <iostream>
using namespace std;

//通过全局函数 打印Person信息
template<class T1,class T2>
class Person
{
    //全局函数类内实现
    friend void printPerson1(Person<T1,T2>p)
    {
        cout << "类内实现 --- 姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
    }
    //全局函数类外实现
    //类外实现要加上template,否则不能类外实现
    template<class t1,class t2>
    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;
};

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

//全局函数在类内实现
void test01()
{
    Person<string ,int>p("Jerry",20);
    printPerson1(p);
}

//全局函数在类外实现
void test02()
{
    Person<string,int>p("Tim",28);
    printPerson2(p);
}

int main() {
    test01();
    test02();
    return 0;
}
类模版案例 

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

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

主函数如下: 

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

class Person {
public:
    // 默认构造函数
    Person() : name(""), age(0) {};
    // 构造函数声明
    Person(string m_name, int m_age);

    // 打印函数用于测试
    void print() const;

    string name;  // 姓名
    int age;      // 年龄
};

// 构造函数定义
Person::Person(string m_name, int m_age) : name(m_name), age(m_age) {
    // 这里可以进行其他初始化或操作
}

// 打印函数定义
void Person::print() const {
    cout << "Name: " << name << ", Age: " << age << endl;
}

void test01()
{
    MyArray<int>arr1(5);

    for(int i=0;i<5;i++)
    {
        arr1.Push_Back(i);
    }
    for(int i=0;i<arr1.getSize();i++)
    {
        cout << arr1[i] << endl;
    }

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

    MyArray<int>arr2(arr1);
    arr2.Pop_Back();
    for(int i=0;i<arr2.getSize();i++)
    {
        cout << arr2[i] << endl;
    }

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

void test02()
{
    Person p1("小明", 20);
    Person p2("小红", 18);
    Person p3("小亮", 19);

    MyArray<Person> arr1(10);
    arr1.Push_Back(p1);
    arr1.Push_Back(p2);
    arr1.Push_Back(p3);

    for (int i = 0; i < arr1.getSize(); i++)
    {
        Person& p = arr1[i]; // 从 MyArray 中获取 Person 对象
        cout << "姓名: " << p.name << ", 年龄: " << p.age << endl;
    }
    cout << "arr1的大小:" << arr1.getSize() << endl;
    cout << "arr1的容量:" << arr1.getCapacity() << endl;
}

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

 MyArray实现如下:

#pragma once
#include "iostream"
using namespace std;

template<class T>
class MyArray
{
public:
    //有参构造 参数 容量
    MyArray(int capacity)
    {
        cout << "有参构造" << endl;
        this->m_Capacity=capacity;
        this->m_Size=0;
        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=new T[arr.m_Capacity];
        //将原来数组数据都拷贝过来
        for(int i=0;i<arr.m_Size;i++)
        {
            this->pAddress[i]=arr.pAddress[i];
        }
    }
    //operator= 防止浅拷贝问题
    MyArray& operator=(const MyArray& arr)
    {
        cout << "operator=调用" <<endl;
        if (this->pAddress!= nullptr)
        {
            delete[] this->pAddress;
            this->pAddress= nullptr;
        }

        //深拷贝
        this->m_Capacity=arr.m_Capacity;
        this->m_Size=arr.m_Size;
        this->pAddress=new T[arr.m_Capacity];
        for(int i=0;i<arr.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;
    }
    //尾删法
    void Pop_Back()
    {
        if(this->m_Size==0)
        {
            return;
        }
        this->m_Size--;
    }
    //通过下标方式访问数组中的元素
    T& operator[](int index)
    {
        return this->pAddress[index];
    }
    //返回数组容量
    int getCapacity()
    {
        return this->m_Capacity;
    }
    //返回数组长度
    int getSize()
    {
        return this->m_Size;
    }

    //析构函数
    ~MyArray()
    {
        if(this->pAddress!= nullptr)
        {
            cout << "析构调用" << endl;
            delete[] this->pAddress;
            this->pAddress= nullptr;
        }
    }

private:
    T* pAddress;//指针指向堆区开辟的真实数组
    int m_Capacity;//数组容量
    int m_Size;//数组大小
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值