函数模板

函数模板语法

普通函数实现交换


void swap1(int &a,int &b)
{
    int t=a;
    a=b;
    b=t;
}
void swap2(double &a,double &b)
{
    double t=a;
    a=b;
    b=t;
}



void test()
{
    int a=10,b=20;
    double c=-10,d=-20;
    swap1(a,b);
    swap2(c,d);
    cout<<"a=  "<<a<<" b=  "<<b<<endl;
    cout<<"c=  "<<c<<" d=  "<<d<<endl;
    myswap(a,b);
    myswap(c,d);
    cout<<"a="<<a<<" b="<<b<<endl;
    cout<<"c="<<c<<" d="<<d<<endl;
}

函数模板写法

template<typename T>
void myswap(T &t1,T &t2)
{
    T t=t1;
    t1=t2;
    t2=t;
}

函数模板有两种使用方式,自动类型推导和显示指示类型; 显示指示类型就是要在使用时候加上关键字 比如 myswap<int>(a,b);

函数模板的注意事项

使用自动类型推导时,一定要推导出一直的数据类型

下面这种使用方法报错

模板必须确定T的数据类型 模板不能独立使用,必须确定通用数据类型,并且能推导出一致的类型

这个可以理解为。 看下面这里的函数根本没有调用T,但是还是会报错。所以必须要指定一个T,他才会运行

下面这串代码成功运行 无报错

template<class T>
void func()
{
    cout<<"func函数调用"<<endl;
}
void test()
{
    func<int>();
}

函数模板案例

使用选择排序,写出一个从小到大的选择排序模板

template<class T>
void SWAP(T &a,T &b)
{
    T t=a;
    a=b;
    b=t;
}


template<class T>
void SORT(T a[],int len)
{
    for(int i=0;i<len;i++)
    {
        int max=i;
        for(int j=i+1;j<len;j++)
        {
            if(a[j]>a[max])
            {
                max=j;
            }

        }
        if(max!=i)
        {
            SWAP(a[max],a[i]);
        }
    }
}


void test()
{
   int a[]={1,2,3,4,5,6,7,8};
    SORT(a,8);
    for(int i=0;i<8;i++)
    {
        cout << a[i] << " ";
    }
    cout<<endl;
    char b[]="abcdefghi";
    SORT(b,9);
    for(int i=0;i<9;i++)
    {
        cout << b[i] << " ";
    }
    cout<<endl;
}

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

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

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)

  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换

  • 如果利用显示指定类型的方式,可以发生隐式类型转换 直接转换为你指定的类型,如果不行就报错


可以看到普通函数不报错但是类模板报错

但是如果我们给他显示指示类型,

int add1(int a,int b)
{
    return a+b;
}

template<class T>
T add2(T a,T b)
{
    return a+b;
}


void test()
{
    int a=1;
    char c='a';
    cout<<add1(a,c)<<endl;
    cout<<add2<int>(a,c)<<endl;
}

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

  1. 如果函数模板和普通函数都可以实现,优先调用普通函数

  2. 可以通过空模板参数列表来强制调用函数模板

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

  4. 如果函数模板可以产生更好的匹配,优先调用函数模板

可以看到第四个重载模板函数调用,调用普通函数要发生隐式类型转换,而模板不用

void Print(int a)
{
    cout<<"普通函数调用"<<endl;
}
template<class T>
void Print(T a)
{
    cout<<"模板函数调用"<<endl;
}
template<class T>
void Print(T a,T b,T c=10)
{
    cout<<"重载模板函数调用"<<endl;
}

void test()
{
    int a=10;
    Print(a);
    Print<>(a);
    int b=10;
    Print(a,b);
    char c,d;
    Print(c,d);
}

模板函数的局限性

提供一个专供person版本的mycompare函数

注意写法 template<> bool mycompare(person &a,person &b);括号里直接给他指明是传入person用这个函数

这是一种模板函数的重载,只不过是提供给特定类型,的具体化模板

class person
{
public:
    person(string a,int b)
    {
        name=a;
        age=b;
    }

    string name;
    int age;
};

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



template<>bool  mycompare(person &a,person &b)
{
    if(a.name==b.name&&a.age==b.age)
        return 1;
    else return 0;
}


void test()
{
    person a("刘帅",10);
    person b("张帅",20);
    if(mycompare(a,b))
    {
        cout<<"相等"<<endl;
    }
    else cout<<"不相等"<<endl;
}

类模板

类模板语法

这个可以简单理解为 自定义类里面的参数类型,但是在传入的时候要指定

template<class NAME,class AGE>
class person
{
public:
    person(NAME a,AGE b)
    {
        name=a;
        age=b;
    }
    void show()
    {
        cout<<name<<" "<<age<<endl;
    }
    NAME name;
    AGE age;
};
void test()
{
  person<string,int>p1("刘帅",22);
    person<char,float>p2('A',22.2);
    p1.show();
    p2.show();
}

类模板和函数模板的区别

  • 类模板没有自动类型推导的使用方式

这个可以在使用的时候可以看出来,类模板使用必须使用 显示指示类型

  • 类模板在模板参数列表中可以有默认参数

template<class NAME,class AGE=int>
class person
{
public:
    person(NAME a,AGE b)
    {
        name=a;
        age=b;
    }
    void show()
    {
        cout<<name<<" "<<age<<endl;
    }
    NAME name;
    AGE age;
};


void test()
{
  person<string,int>p1("刘帅",22);
    person<char,float>p2('A',22.2);
    person<string>p3("帅帅",22);
    p1.show();
    p2.show();
    p3.show();
}

类模板成员函数创建时机

在这里我们创建2个类,然后再创建一个类模板分别调用这两个类中的成员函数

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 fun1(){obj.showperson1();}
    void fun2(){obj.showperson2();}
};

可以看到出现报错,这里因为我们使用的类模板指定用person1的类,

类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成 调用时候再去创建成员函数

类模板对象作函数参数

1,指定传入类型 直接在函数传参里面传入模板 告诉他模板的参数类型

template<class Nanmetype,class Agetype>
class person
{
public:
    person(Nanmetype a,Agetype b)
    {
        name=a;
        age=b;
    }
    void show()
    {
        cout<<"name:"<<name<<" age:"<<age<<endl;
    }
    Nanmetype name;
    Agetype age;
};

//1 指定传入类型
void Print(person<string,int>&p)
{
    p.show();
}
void test()
{
  person<string,int>p("刘帅",22);
  Print(p);
}

2,参数模板化

上面是将指定参数类型的类模板传进函数,这里是直接将参数也变成模板 但是要在上面加一个模板

typeid().name是将参数名输出

template <class T1,class T2>
void Print(person<T1,T2>&p)
{
    p.show();
    cout<<"T1的数据类型为:"<< typeid(T1).name()<<endl;
    cout<<"T2的数据类型为:"<< typeid(T2).name()<<endl;
}

3,整个类模板化

template<class T>
void Print(T &p)
{
    p.show();
    cout<<"T的类型为:"<<typeid(T).name()<<endl;
}

后面2种相当于函数模板配合类模板 有点复杂搞不清 反正一般用第一种

类模板与继承

这里继承Base打完马上会自动弹出<>,要求你指定父类T中的数据类型

c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承

父类是类模板,子类要继承,也可以把子类变成一个类模板,在这里继承的时候将继承过来的BASE中的数据类型也当作模板

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

class Son:public Base<int>
{

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

void test()
{
    Son2<int,string> s;
}

类模板成员函数类外实现

这里想到类里面的成员函数类外实现,掌握语法怎么写

其实这里和普通类实现差不多,类内都是只留下声明; 类外实现因为模板有T1,T2,所以要在上面加一行template<class T1,class T2>,告诉函数这是类模板里面的参数。 然后普通作用域直接person::完事,这里要写清楚person<T1,T2>::

template<class T1,class  T2>
class person
{
public:
    person(T1 n,T2 age);
    void show();
    T1 name;
    T2 age;
};
template<class T1,class  T2>
person<T1,T2>::person(T1 n,T2 a)
{
    name=n;
    age=a;
}


template<class T1,class  T2>
void person<T1, T2>::show()
{
    cout<<name<<"  "<<age<<endl;
}

void test()
{
    person<string,int>p("刘帅",22);
    p.show();
}

类模板分文件编写

如果我们和之前一样设置一个person.h和person.cpp,然后用h内只声明,.cpp写实现,在代码写好后编译器不会出现任何报错,但是点构建或者运行会报错。
解决方法1 这样正常输出

#include "person.cpp"

解决方法2 将声明和实现写在一起 改后缀名hpp 在包含

为什么会这样? 因为类模板的函数是在编译的时候在去实现,正常分文件写,编译器读到头文件之后没读到cpp就出错了,不知道这些函数用什么,但是直接包含cpp,直接读类模板的函数。

类模板与友元

案例

记得深浅拷贝问题

template<class T>
class Myarr
{
public:
    //有参构造
    Myarr(int max)
    {
        this->max_num=max;
        this->len=0;
        this->p=new T[max];
        cout<<"有参构造函数"<<endl;
    }
    //析构
    ~Myarr()
    {
        if(this->p!=NULL)
            delete[] this->p;
        this->p=NULL;
        this->len=0;this->max_num=0;
        cout<<"析构函数"<<endl;
    }
    //拷贝构造
    Myarr(const Myarr &arr)
    {
        this->max_num=arr.max_num;
        this->len=arr.len;
        this->p=new T[arr.max_num];
        for(int i=0;i<arr.len;i++)
        {
            this->p[i]=arr.p[i];
        }
        cout<<"拷贝函数"<<endl;
    }
    //重载=
   Myarr& operator=(const Myarr &arr)
    {
        if(this->p!=NULL)
        {
            delete[] this->p;
            this->p=NULL;
            this->len=0;this->max_num=0;
        }
        this->max_num=arr.max_num;
        this->len=arr.len;
        this->p=new T[arr.max_num];
        for(int i=0;i<arr.len;i++)
        {
            this->p[i]=arr.p[i];
        }
        cout<<"=重载函数"<<endl;
        //!!!
        return *this;
    }
    //尾插法
    void push_back(const T &v)
    {
        if(this->max_num==this->len)
        {
            cout<<"容量已满"<<endl;
            return;
        }
        this->p[this->len++]=v;
    }
    //尾删法
    void pop_back()
    {
        if(this->len==0)
        {
            return;
        }
        this->len--;
    }
    //访问元素
    T& operator[](int index)
    {
        return this->p[index];
    }
    //返回容量
    int getmax_num()
    {
        return this->max_num;
    }
    //返回长度
    int get_len()
    {
        return this->len;
    }
private:
    T *p;
    int max_num;
    int len;
};
void test()
{
    Myarr<int>arr(5);
    Myarr<int>arr1(arr);
    Myarr<int>arr3(100);
    arr3=arr;
}

template<class T>
class Myarr
{
public:
    //有参构造
    Myarr(int max)
    {
        this->max_num=max;
        this->len=0;
        this->p=new T[max];
        cout<<"有参构造函数"<<endl;
    }
    //析构
    ~Myarr()
    {
        if(this->p!=NULL)
            delete[] this->p;
        this->p=NULL;
        this->len=0;this->max_num=0;
        cout<<"析构函数"<<endl;
    }
    //拷贝构造
    Myarr(const Myarr &arr)
    {
        this->max_num=arr.max_num;
        this->len=arr.len;
        this->p=new T[arr.max_num];
        for(int i=0;i<arr.len;i++)
        {
            this->p[i]=arr.p[i];
        }
        cout<<"拷贝函数"<<endl;
    }
    //重载=
   Myarr& operator=(const Myarr &arr)
    {
        if(this->p!=NULL)
        {
            delete[] this->p;
            this->p=NULL;
            this->len=0;this->max_num=0;
        }
        this->max_num=arr.max_num;
        this->len=arr.len;
        this->p=new T[arr.max_num];
        for(int i=0;i<arr.len;i++)
        {
            this->p[i]=arr.p[i];
        }
        cout<<"=重载函数"<<endl;
        //!!!
        return *this;
    }
    //尾插法
    void push_back(const T &v)
    {
        if(this->max_num==this->len)
        {
            cout<<"容量已满"<<endl;
            return;
        }
        this->p[this->len++]=v;
    }
    //尾删法
    void pop_back()
    {
        if(this->len==0)
        {
            return;
        }
        this->len--;
    }
    //访问元素
    T& operator[](int index)
    {
        return this->p[index];
    }
    //返回容量
    int getmax_num()
    {
        return this->max_num;
    }
    //返回长度
    int get_len()
    {
        return this->len;
    }
private:
    T *p;
    int max_num;
    int len;
};
void test()
{
    Myarr<int>arr(5);
    for(int i=0;i<5;i++)
    {
        arr.push_back(i);
    }
    for(int i=0;i<5;i++) cout<<arr[i]<<" ";
    cout<<endl;
    cout<<"arr的长度为"<<arr.get_len()<<endl;
    cout<<"arr的大小为"<<arr.getmax_num()<<endl;

    arr.pop_back();
    for(int i=0;i<arr.get_len();i++) cout<<arr[i]<<" ";
    cout<<endl;
    cout<<"arr的长度为"<<arr.get_len()<<endl;
    cout<<"arr的大小为"<<arr.getmax_num()<<endl;
}

测试自定义数据类型

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

void printPersonArray(Myarr<Person>& personArr)
{
    for (int i = 0; i < personArr.get_len(); i++) {
        cout << "姓名:" << personArr[i].m_Name << " 年龄: " << personArr[i].m_Age << endl;
    }

}




void test()
{
    Myarr<Person> pArray(10);
    Person p1("孙悟空", 30);
    Person p2("韩信", 20);
    Person p3("妲己", 18);
    Person p4("王昭君", 15);
    Person p5("赵云", 24);
    pArray.push_back(p1);
    pArray.push_back(p2);
    pArray.push_back(p3);
    pArray.push_back(p4);
    pArray.push_back(p5);
    printPersonArray(pArray);
    cout << "pArray的大小:" << pArray.get_len() << endl;
    cout << "pArray的容量:" << pArray.getmax_num() << endl;
}