C++类模板

1 模板

1.1 模板的概念

模板:所谓模板就是建立通用的摸具,大大提高复用性

模板的特点:

  • 模板不可以直接使用,它只是一个框架
  • 模板的通用性并不是万能的

1.2 函数模板

  • C++另一种编程思想称为泛型编程,主要利用的技术就是模板
  • C++提供两种模板机制:函数模板类模板

1.2.1 函数模板语法

函数模板的作用:

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

语法:

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

例子:

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

解释:

template ---- 声明创建模板

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

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

总结:

  • 函数模板利用关键字template

  • 使用函数模板有两种方式:

    1、自动类型推导

    ​ 调用:例:函数名(a, b)

    2、显示指定类型

    ​ 调用:例:函数名(a, b)

  • 模板的目的是为了提供复用性,将类型参数化

1.2.2 函数模板注意事项

注意:

  • 若以自动类型推导使用函数模板,函数的数据类型必须一致
  • 模板必须要确定出T的数据类型,才可以使用

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

三个区别:

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

总结:

因以上的差别,所以推荐在调用函数模板时要使用指定类型的方式,清楚明了

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

规则:

  • 如果函数模板和普通函数均可调用(同名),优先调用普通函数
  • 可以通过空模板参数列表,强制调用函数模板 语法:函数名<>(参数1, 参数)
  • 函数模板也可以发生重载
  • 如果函数模板有更好的匹配参数,优先匹配函数模板(简化隐式类型转换)

1.2.5 函数模板的局限性

局限性:模板的通用性并不是万能的,对于一些特定的数据类型,或自定义的数据类型,不做处理很难实现。

比如:普通赋值操作函数,传入的参数为数组类型或者为自定义类型时无法实现函数功能

解决方法:

1、运算符重载

2、具体化参数模板(在原有的函数模板后,加上具体类型模板) 语法:template<> 返回类型 函数名(具体类型 参数1, 具体类型 参数2){}

总结:

  • 利用具体化的模板,可以解决自定义类型的通用化,解决特殊数据类型参数
  • 学习模板并不是为了写模板,而是在STL中能够运用系统提供的模板

1.3 类模板

类模板的作用:

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

1.3.1 类模板语法

语法:

template<class T>
class 类名{}

示例:

#include 
using namespace std;
template<class AgeTpye, class NameTpye>
class Person
{
public:
    AgeTpye age;
    NameTpye name;
    Person(AgeTpye age, NameTpye name)
    {
        this->age = age;
        this->name = name;
    }
    void show()
    {
        cout << this->name << endl;
        cout << this->age << endl;
    }

};
int main()
{
    Person<int, string>p(15, "张三");
    p.show();
    return 0;
}

1.3.2 类模板与函数模板区别

主要有两点区别:

  1. 类模板中没有自动类型推导的使用方法(必须指定模板类型)
  2. 类模板在模板参数列表中可以有默认参数 //示例:template

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

类模板中的成员函数和普通类中的成员函数区别:

  1. 普通类中的成员函数一开始就可以创建
  2. 类模板中的成员函数在调用时才创建

示例:

#include 
using namespace std;
class Person1
{
public:
    void showperson1()
    {
        cout << "调用了showperson1" << endl;
    }
};
class Person2
{
public:
    void showperson2()
    {
        cout << "调用了showperson2" << endl;
    }
};
template<class T>
class Person
{
public:
    T obj;

    void fun1()
    {
        obj.showperson1();
    }
    void fun2()
    {
        obj.showperson2();
    }
};
int main()
{
    Person<Person2>p;
    p.fun1();
    //p.fun2();   //会出错因为实例化对象时指定的类型为Person1
    return 0;
}

1.3.4 类模板对象做函数参数

含义:类模板实例化出的对象,向函数传参的方式

一般有三种传参方式:

  1. 指定传入的类型 ---- 直接指定对象的数据类型
  2. 参数模板化 ---- 将对象中的参数变为模板进行传参
  3. 整个类模板化 ---- 将整个对象类型模板化进行传参

后两者方法相当于类模板结合函数模板使用

示例:

#include 
using namespace std;
template<class T1, class T2>
class Person
{
public:
    T1 name;
    T2 age;

    Person(T1 name, T2 age)
    {
        this->age = age;
        this->name = name;
    }
    void showPerson()
    {
        cout << this->name << endl;
        cout << this->age << endl;
    }
};
//1、指定传入类型
void printPerson1(Person<string, int> & p)
{
    p.showPerson();
}
//2、参数模板化
template<class T2, class T1>
void printPerson2(Person<T2, T1>& p)
{
    p.showPerson();
}
//3、把整个类都模板化
template<class T>
void printPerson3(T & p)
{
    p.showPerson();
}
int main()
{
    Person<string, int>p1("张三", 18);
    printPerson3(p1);
    return 0;
}

总结:

  • 三种传参方法,中最常用的是第一种:直接指定对象的数据类型

1.3.5 类模板与继承

当类模板碰到继承时,需要注意一下几点:

1、当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
2、如果不指定,编译器无法给子类分配内存
3、如果想灵活指定出父类中T的类型,子类也需变为类模板

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

方法:

在类内声明成员函数,在类外实现成员函数内容,在添加作用域时需要加上模板的参数列表。

示例:

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

    void showPerson();

};

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->age = age;
    this->name = name;
}
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << this->age << endl;
    cout << this->name << endl;
}
int main()
{
    return 0;
}

1.3.7 类模板分文件编写

存在问题:

因为类模板中的成员函数的创建时机是在调用阶段,而份文件编写类时,在.h文件中存放类的属性和成员函数声明,在.cpp文件中实现成员函数的实现。而在调用主cpp时只包含.h文件,在调用时因为.cpp中的函数都没有创建,编译器只看到了.h文件中的函数声明,所以会引发异常

解决方法:

  1. 直接包含.cpp源文件 (这样编译器既看到了类声明,也看到了成员函数)
  2. 将类.h文件和实现类中的函数.cpp文件写在同一个文件中,并更改后缀名为.hpp (.hpp是约定俗成的,并不是强制的,而是让读代码的人可以知道只是一个类模板的份文件编写)

1.3.8 类模板与友元

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

全局函数类外实现:需要提前让编译器知道全局函数的存在和类模板的存在

你可能感兴趣的:(C++,C++模板,函数模板,类模板,c++)