如果代码逻辑相同,可以将参数列表的数据类型进行抽象
template <typename T>
返回类型 函数名(参数列表) {
// 函数体
}
//如果想要定义多种数据类型,可在参数列表多次定义,即
template <typename T,typename W>
返回类型 函数名(参数列表) {
// 函数体
}
其中,template 是声明模板的关键字,typename (也可以用 class)用于指定类型参数 T,T 代表一个通用类型。在函数定义中,可以使用 T 作为一种数据类型
#include
using namespace std;
// 定义一个函数模板,用于交换两个变量的值
template <typename T>
void swapValues(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main() {
int num1 = 10, num2 = 20;
cout << "交换前: num1 = " << num1 << ", num2 = " << num2 <<endl;
//1.自动推导类型为int
swapValues(num1, num2);
cout << "交换后: num1 = " << num1 << ", num2 = " << num2 << endl;
double d1 = 3.14, d2 = 2.71;
cout << "交换前: d1 = " << d1 << ", d2 = " << d2 << endl;
//2.显示指定类型为double
swapValues<double>(d1, d2);
cout << "交换后: d1 = " << d1 << ", d2 = " << d2 << endl;
return 0;
}
注意事项:
#include
using namespace std;
// 定义一个函数模板,用于交换两个变量的值
template <typename T >
void func()
{
cout << "函数模板的调用" << endl;
}
int main()
{
func<int>();//随意指定了一个int类型
}
#include
using namespace std;
template <typename T >
void func()
{
cout << "函数模板的调用" << endl;
}
void func()
{
cout << "普通函数的调用" << endl;
}
int main()
{
func<int>();
}
3.函数模板也可以发生重载
4.如果产生了更好的匹配,即如果调用普通函数需要发生类型转换,那么就会直接调用函数模板
#include
using namespace std;
template <typename T >
void func(T a)
{
cout << "函数模板的调用" << endl;
}
void func(int a)
{
cout << "普通函数的调用" << endl;
}
int main()
{
char a='a';
func(a);//由于调用普通函数需要类型转换,就直接调用函数模板
}
这里只针对模板对于一些数组或者类作为参数的情况,函数模板无法正确运行了;我们可以运用以前学过的运算符重载,不过那会显得非常麻烦,所以这里就有了函数模板的重载,即提供一个具体化的模板
#include
#include
using namespace std;
//对比两个变量是否相等
template<class T>
void is_equal(T& a, T& b)
{
if (a == b)cout << "a==b" << endl;
else cout << "a!=b" << endl;
}
class person
{
public:
person(string name, int age)
{
this->name = name;
this->age = age;
}
string name;
int age;
};
template<> void is_equal(person & p1, person & p2)
{
if (p1.name == p2.name&&p1.age==p2.age)cout << "p1==p2" << endl;
else cout << "p1!=p2" << endl;
}
int main()
{
person p1("tom", 10);
person p2("jero", 20);
is_equal(p1, p2);
}
类模板其实就是和函数模板类似,不过是将成员变量的数据类型进行了抽象,下面是一个简单例子:
#include
#include
using namespace std;
//对比两个变量是否相等
template<class nameType,class ageType>
class person
{
private:
nameType name;
ageType age;
public:
person(nameType name, ageType age)
{
this->name = name;
this->age = age;
}
void show()
{
cout << "name:" << this->name << endl << "age:" << this->age << endl;
}
};
int main()
{
person<string, int> p1("tom", 10);//显示指定类型
p1.show();
}
需要注意的是类模板和函数模板不一样,类模板没有自动类型转换,必须我们进行显示指定类型;其次类模板在模板参数列表中可以有默认参数,即在类模板参数列表定义中可以设置一个默认参数template
,这和之前的默认函数参数规则是一样的,不过要注意的是如果我们全部使用了默认参数也要在调用时写上空列表即<>
#include
#include
using namespace std;
class person1
{
public:
void show1()
{
cout << "person1 show" << endl;
}
};
class person2
{
public:
void show2()
{
cout << "person2 show" << endl;
}
};
template<class T>
class person
{
public:
T obj;
void func1() { obj.show1(); }
void func2() { obj.show2(); }
};
int main()
{
person<person1> p1;
p1.func1();
//p1.func2();报错:已经指定了person1类型,无法调用person2中的内容
}
上方代码证明了在不指定模板中的具体类型时就不会创建成员函数,因为还无法知道这个类是指代哪一个具体类;而普通函数或者成员函数一般是在编译阶段就全部编译了
以下是类模板对象作为函数参数的三种传参方式
#include
#include
using namespace std;
template<class nameType,class ageType>
class person
{
public:
nameType name;
ageType age;
person(nameType name,ageType age)
{
this->name = name;
this->age = age;
}
void show()
{
cout << this->name << endl << this->age << endl;
}
};
//1.指定传入类型
void print1(person<string, int>& p1)
{
cout << "函数1的调用" << endl;
p1.show();
}
//2.参数模板化
template<class t1,class t2>
void print2(person<t1, t2>& p2)
{
cout << "函数2的调用" << endl;
p2.show();
}
//3.整个类模板化
template<class t>
void print3(t& p3)
{
cout << "函数3的调用" << endl;
p3.show();
}
int main()
{
person<string, int>p1("tom", 20);
print3<person<string,int>>(p1);
//或者直接让函数模板进行自动推导
//即print3(p1);
}
实际开发中最常用的还是第一种直接指定参数类型
#include
#include
using namespace std;
template<class T>
class base
{
T name;
};
class son :public base<string>//必须指定基类模板参数类型
{
//类成员……
};
#include
#include
using namespace std;
template<class T>
class base
{
T name;
};
template<class T1,class T>
class son :public base<T>//将基类模板参数指定为子类模板参数,使其数据类型更加灵活
{
T1 age;
};
int main()
{
son<string, int>son1;
}
不仅要告知编译器模板参数的存在,还要告知其不为普通函数的类外实现,而是函数模板的类外实现
#include
#include
using namespace std;
template<class T>
class person
{
public:
person(T name)
{
this->name = name;
}
T name;
void show();
};
template<class T>//告知模板参数的存在
void person<T>::show()//告知其为类模板的类外实现
{
cout << this->name << endl;
}
int main()
{
person<string>p1("tom");
p1.show();
}
如果是全局函数在类内实现友元函数那么直接实现就可以了,但是如果是在类外实现友元函数,我们需要提前让编译器知道全局函数的存在
全局函数做友元类内实现:
#include
#include
using namespace std;
template<class T>
class person
{
public:
person(T name)
{
this->name = name;
}
private:
//全局函数类内实现
friend void show(person<T>p1)
{
cout << p1.name << endl;
}
T name;
};
template<class T>
void show(person<T>p1);
int main()
{
person<string>p1("tom");
show(p1);
}
全局函数做友元类外实现:
#include
#include
using namespace std;
// 提前声明 person 类模板
template<class T>
class person;
// 提前声明 show 函数模板
template<class T>
void show(person<T> p1);
template<class T>
class person
{
public:
person(T name)
{
this->name = name;
}
private:
// 全局函数是一个函数模板,所以这里加了<>
friend void show<>(person<T> p1);
T name;
};
// 实现 show 函数模板
template<class T>
void show(person<T> p1)
{
cout << p1.name << endl;
}
int main()
{
person<string> p1("tom");
show(p1);
return 0;
}