学习C++:C++基础(三)泛型编程&C++模板

目录

1.0 模板概论

1.1 函数模板

1.1.1 模板声明及模板使用小案例

1.1.2 函数模板和普通函数的区别以及调用规则

1.1.3 函数机制和模板局限性

1.1.4 类模板

1.1.5 案例:myarray


1.0 模板概论

        C++提供了函数模板。所谓函数模板,实际上是建立了一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就是函数模板。

        凡是函数体相同的函数都可以用这个模板代替,不必定义多个函数,只要在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现不同函数的功能。

1.1 函数模板

1.1.1 模板声明及模板使用小案例

1.实现任意两个数交换

void myswap(T& a,T& b)
{
    T temp = a;
    a = b;
    b = temp;
}

void test01()
{
    int a = 1;
    int b = 2;
    float c = 1.1;
    float d = 2.2;
    myswap(a,b);
    myswap(c,d);
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl;
    cout << "d = " << d << endl;
}
/home/lhw/桌面/C++/day8/cmake-build-debug/day8
a = 2
b = 1
c = 2.2
d = 1.1

2.模板声明

template<typename T>

这里的T是指代通用数据类型,告诉编译器如果下面的函数或者类中出现T不要报错。

3.模板使用

I.自动类型推导

自动判断a,b是int类型,必须推导出是一致的T数据类型,才能正确的使用模板。

II.显示指定类型

void test01()
{
    int a = 1;
    int b = 2;
    float c = 1.1;
    float d = 2.2;
    myswap<int>(a,b);
    myswap<float>(c,d);
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl;
    cout << "d = " << d << endl;
}

III.模板不能单独使用,必须指定T才可以使用

4.案例:对char和int数组进行排序

void myswap(T& a,T& b)
{
    T temp = a;
    a = b;
    b = temp;
}


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

template<typename T>
void printarray(T arr[],int len)
{
    for(int i=0;i<len;i++)
        cout << arr[i] ;
    cout << endl;
}

void test02()
{
    char chararray[] = "helloworld";
    int charlen = strlen(chararray);
    mySort(chararray,charlen);
    printarray(chararray,charlen);

    int intarray[] = {5,9,7,4,3,6,8};
    mySort(intarray,7);
    printarray(intarray,7);
}
/home/lhw/桌面/C++/day8/cmake-build-debug/day8
wroolllhed
9876543

1.1.2 函数模板和普通函数的区别以及调用规则

1. 函数模板和普通函数的区别

void test03()
{
    int a = 10;
    int b = 20;
    char c = 'c';
    //myadd1(a,c);  error 如果使用自动类型推导,是不允许发生隐式类型转换的
    cout << myadd2(a,c) << endl;    //普通函数允许发生隐式类型转换
}

函数模板如果使用自动类型推导,是不允许发生隐式类型转换的。

普通函数允许发生隐式类型转换

2. 函数模板和普通函数的调用规则

I.如果函数模板和普通函数都可以调用,那么优先调用普通函数。

template<typename T>
void printmt(T a,T b)
{
    cout << "template function is running" << endl;
}

void printmt(int a,int b)
{
    cout << "original function is running" << endl;
}

void test04()
{
    
/home/lhw/桌面/C++/day8/cmake-build-debug/day8
original function is running

II.如果想强制调用函数模板,可以使用空模板参数列表

template<typename T>
void printmt(T a,T b)
{
    cout << "template function is running" << endl;
}

void printmt(int a,int b)
{
    cout << "original function is running" << endl;
}

void test04()
{
    printmt<>(10,20);
}

III.函数模板也可以发生函数重载

template<typename T>
void printmt(T a,T b)
{
    cout << "template function 2 is running" << endl;
}

template<typename T>
void printmt(T a,T b,T c)
{
    cout << "template function 3 is running" << endl;
}

void printmt(int a,int b)
{
    cout << "original function is running" << endl;
}

void test04()
{
    printmt<>(10,20,30);
}
/home/lhw/桌面/C++/day8/cmake-build-debug/day8
template function 3 is running

IV.如果函数模板能产生更好的转换,优先选择函数模板

template<typename T>
void printmt(T a,T b)
{
    cout << "template function 2 is running" << endl;
}

template<typename T>
void printmt(T a,T b,T c)
{
    cout << "template function 3 is running" << endl;
}

void printmt(int a,int b)
{
    cout << "original function is running" << endl;
}

void test04()
{
    printmt<>('a','b');
}
/home/lhw/桌面/C++/day8/cmake-build-debug/day8
template function 2 is running

1.1.3 函数机制和模板局限性

1.局限性

I.编译器并不是把函数模板处理成能够处理任何类型的数据---通过具体化解决

代码:看两个变量是否相等

class Person
{
public:
    Person(string name,int age)
    {
        this->name = name;
        this->age = age;
    }
    string name;
    int age;
};


template<class T>
bool mycompare(T& a,T& b)
{
    if(a==b)
        return true;
}

template<> bool mycompare(Person& a,Person& b)
{
    if(a.name==b.name && a.age==b.age)
        return true;
    else
        return false;
}

void test05()
{
    int a = 10;
    int b = 10;
    Person p1("Tom",19);
    Person p2("Tom",19);
    bool ret = mycompare(a,b);
    if(ret)
    {
        cout << "a = b" << endl;
    }
    else
    {
        cout << "a !=b ";
    }
}

II.函数模板通过具体类型产生不同的函数

template<class T>
bool mycompare(T& a,T& b)
{
    if(a==b)
        return true;
}

template<> bool mycompare(Person& a,Person& b)
{
    if(a.name==b.name && a.age==b.age)
        return true;
    else
        return false;
}
/home/lhw/桌面/C++/day8/cmake-build-debug/day8
a = b

III.编译器会对函数模板进行再次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。

1.1.4 类模板

1.声明

template<class NAMETYPE,class AGETYPE>
class Person
{
public:
    Person(NAMETYPE name,AGETYPE age)
    {
        this->name = name;
        this->age = age;
    }
    void showperson()
    {
        cout << "name is : " << this->name << endl;
        cout << "age is : " << this->age << endl;
    }
    NAMETYPE name;
    AGETYPE age;
};

我们可以有多种模板声明,函数模板也可以。

2.调用

I.自动类型推导:类模板不可以用(对比于函数模板)

    Person p1("sunwukong",100);

这是错误的,在函数模板可以用在类模板不可以用。

II.指定类型

void test01()
{
    Person <string,int>p1("sunwukong",100);
    p1.showperson();
}
/home/lhw/桌面/C++/day8/cmake-build-debug/day8
name is : sunwukong
age is : 100

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

class Person1
{
public:
    void showperson1()
    {
        cout << "personshow1 is running" << endl;
    }
};

class Person2
{
public:
    void showperson2()
    {
        cout << "personshow2 is running" << endl;
    }
};

template<class T>
class Myclass
{
public:
    void Func1()
    {
        obj.showperson1();
    }
    void Func2()
    {
        obj.showperson2();
    }
    T obj;
};


void test01()
{
    Myclass<Person1> p1;
    p1.Func1();
    //p1.Func2();
    Myclass<Person2> p2;
    //p2.Func1();
    p2.Func2();
}

我们可以看到,p1调用不了Func2,p2调用不了Func1。

类模板中成员函数并不是一开始创建的,创建时机是在运行阶段确定出T的类型才创建的。

4.类模板做函数参数

I.指定传入类型

template<class T1,class T2>
class Person
{
public:
    Person(T1 name,T2 age)
    {
        this->name = name;
        this->age = age;
    }
    void showperson()
    {
        cout << "name is : " << this->name << endl;
        cout << "age is : " << this->age << endl;
    }
    T1 name;
    T2 age;
};

void dowork(Person<string,int>&p)
{
    p.showperson();
}

void test01()
{
    Person<string,int>p("sunwukong",99);
    dowork(p);
}
/home/lhw/桌面/C++/day8/cmake-build-debug/day8
name is : sunwukong
age is : 99

不用编译器猜,自己提供数据类型

II.参数模板化

template<class T1,class T2>
void dowork(Person<T1,T2>&p)
{
    p.showperson();
}


void test02()
{
    Person<string,int>p("zhubajie",98);
    dowork(p);
}
/home/lhw/桌面/C++/day8/cmake-build-debug/day8
name is : zhubajie
age is : 98

让编译器猜两个数据类型是什么

III.将整个类模板化

template<class T>
void dowork3(T& p)
{
    p.showperson();
}

void test03()
{
    Person<string,int>p("tangseng",10000);
    dowork3(p);
}
/home/lhw/桌面/C++/day8/cmake-build-debug/day8
name is : tangseng
age is : 10000

让编译器猜是哪个类

5.类模板遇到继承及解决

I.必须要指定出父类中T的类型,才能给子类分配内存

template<class T>
class Base
{
public:
    T m_a;
};

class son : public Base<int>
{

};

II.更灵活的方式

template<class T>
class Base
{
public:
    T m_a;
};

template<class T1,class T2>
class son : public Base<T2>
{
public:
    T1 m_b;
};

void test04()
{
    son <int ,double> s;
}
//m_a double m_b int

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

template<class T1,class T2>
class Person
{
public:
    Person(T1 name,T2 age);
    void showperson();
    T1 name;
    T2 age;
};

template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age)
{
    this->name = name;
    this->age = age;
}

template<class T1,class T2>
void Person<T1,T2>:: showperson()
{
    cout << "name is : " << this->name << endl;
    cout << "age is : " << this->age << endl;
}

7.类模板的分文件编写问题及解决

I.头文件.hpp

//
// Created by lhw on 2022/6/10.
//

#ifndef DAY8_PERSON_H
#define DAY8_PERSON_H

#include <iostream>
using namespace std;
template<class T1,class T2>
class Person
{
public:
    Person(T1 name,T2 age);
    void showperson();
    T1 name;
    T2 age;
};

template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age)
{
    this->name = name;
    this->age = age;
}

template<class T1,class T2>
void Person<T1,T2>:: showperson()
{
    cout << "name is : " << this->name << endl;
    cout << "age is : " << this->age << endl;
}

#endif //DAY8_PERSON_H

II.源文件 main.cpp

#include <cstring>
#include "person.hpp"

void dowork(Person<string,int>&p)
{
    p.showperson();
}

void test01()
{
    Person<string,int>p("sunwukong",99);
    dowork(p);
}


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

8.类模板碰到友元函数及其解决

I.友元函数类内实现

template<class T1,class T2>
class Person
{
public:
    friend void printperson(Person<T1,T2>&p)
    {
        cout << "name is : " << p.name << "age is : " << p.age;
    }

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

private:
    T1 name;
    T2 age;
};


void test03()
{
    Person<string,int>p("tom",100);
    printperson(p);
}

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

可以当作全局函数去调用。

II.友元函数类外实现1:有点复杂

template<class T1,class T2>
class Person;

template<class T1,class T2>
void printperson(Person<T1,T2>&p);

template<class T1,class T2>
class Person
{
public:
    friend void printperson<>(Person<T1,T2>&p);

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

private:
    T1 name;
    T2 age;
};

template<class T1,class T2>
void printperson(Person<T1,T2>&p)
{
    cout << "name is : " << p.name << "age is : " << p.age;
}

void test03()
{
    Person<string,int>p("tom",100);
    printperson(p);
}

III.友元函数类外实现2

template<class T1,class T2>
class Person;

template<class T1,class T2>
void printperson2(Person<T1,T2>&p)
{
    cout << "name is : " << p.name << "age is : " << p.age;
}

template<class T1,class T2>
class Person
{
public:
    friend void printperson2<>(Person<T1,T2>&p);
public:
    Person(T1 name,T2 age)
    {
        this->name = name;
        this->age = age;
    }

private:
    T1 name;
    T2 age;
};

void test03()
{
    Person<string,int>p("tom",100);
    printperson2(p);
}

1.1.5 案例:myarray

1.myarray.hpp

//
// Created by lhw on 2022/6/10.
//

#ifndef DAY8_MYARRAY_HPP
#define DAY8_MYARRAY_HPP

template<class T>
class Myarray
{
public:
    Myarray()
    {

    }
    Myarray(int capacity)
    {
        this -> capacity = capacity;
        this -> size = 0;
        this -> pAddress = new T[this->capacity];
    }
    Myarray(const Myarray & myarray)
    {
        this -> capacity = myarray.capacity;
        this -> size = myarray.size;
        this -> pAddress = new T[this->capacity];
        for (int i = 0 ; i<myarray.size ; i++)
        {
            this -> pAddress[i] = myarray.pAddress[i];
        }
    }
    Myarray& operator=(const Myarray & myarray)
    {
        if(this->pAddress)
        {
            delete []this->pAddress;
            this -> pAddress = NULL;
        }
        this -> capacity = myarray.capacity;
        this -> size = myarray.size;
        this -> pAddress = new T[this->capacity];
        for (int i = 0 ; i<myarray.size ; i++)
        {
            this -> pAddress[i] = myarray.pAddress[i];
        }
        return *this;
    }
    ~Myarray()
    {
        if(this->pAddress)
        {
            delete []this->pAddress;
            this -> pAddress = NULL;
        }
    }
    T& operator[](int index)
    {
        return this->pAddress[index];
    }
    void pushback(const T & value)
    {
        if(this -> capacity == this -> size)
        {
            return;
        }
        this -> pAddress[this->size] = value;
        this ->size ++;
    }
    int getCapacity()
    {
        return this->capacity;
    }
    int getSize()
    {
        return this->size;
    }

private:
    T * pAddress;
    int capacity;
    int size;
};

#endif //DAY8_MYARRAY_HPP

2.main.cpp

#include <iostream>
#include <cstring>

using namespace std;
#include "myarray.hpp"

class Person
{
public:
    Person(){};
    Person(string name,int age)
    {
        this -> name = name;
        this -> age = age;
    }

public:
    string name;
    int age;
};

void myprintint(Myarray<int> & myarray)
{
    for(int i=0;i<myarray.getSize();i++)
    {
        cout << myarray[i] << " ";
    }

}

void test01()
{
    Myarray<int> myintarray(100);
    for(int i = 0;i<10;i++)
    {
        myintarray.pushback(i+100);
    }
    myprintint(myintarray);
}

void myprintPerson(Myarray<Person> & perArr)
{
    for(int i =0 ; i<perArr.getSize() ; i++)
    {
        cout << "name is : " << perArr[i].name << endl << "age is : " << perArr[i].age << endl;

    }
}

void test02()
{
    Myarray<Person> myPersonarray(100);
    Person p1("sunwukong",10);
    Person p2("zhubajie",20);
    Person p3("tangseng",30);
    myPersonarray.pushback(p1);
    myPersonarray.pushback(p2);
    myPersonarray.pushback(p3);
    myprintPerson(myPersonarray);
    cout << "capacity is " << myPersonarray.getCapacity() << endl;
    cout << "size is " << myPersonarray.getSize() << endl;
}
int main() {
    test01();
    test02();
    return 0;
}

3.结果

/home/lhw/桌面/C++/day8/cmake-build-debug/day8
100 101 102 103 104 105 106 107 108 109 name is : sunwukong
age is : 10
name is : zhubajie
age is : 20
name is : tangseng
age is : 30
capacity is 100
size is 3

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

APS2023

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

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

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

打赏作者

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

抵扣说明:

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

余额充值