您现在的位置是:首页 >技术杂谈 >c++ 模板网站首页技术杂谈
c++ 模板
c++ 模板概述
模板函数格式:
template <typename 类型站位符>
返回值类型 函数名(参数列表){}
template是声明模板的关键字,typename 关键字用于标识模板参数,也可以用class关键字代替,class和typename并没有区别。模板参数不能为空,一个函数模板中可以有多个模板参数
template <typename T1,class T2>
T1 add(T1 a, T2 b) {
return a + b;
}
隐式实例化:根据函数调用时传入的参数的数据类型确定模板参数T的类型
void test1() {
double a = 10.087;
int b = 10;
cout << add(a, b) << endl;//add(a,b)隐式实例化
}
显示实例化:将参数转换成指定类型
cout << add<int>(a, b) << endl; //add<int>(a,b)显示实例化
上例中将double类型的a转换成int类型,然后相加
c++模板常见的注意事项
(1)隐式实例化必须推导出同一类型的T才能使用
template <typename T1>
T1 add(T1 a, T1 b) {
return a + b;
}
void test1() {
double a = 10.087;
int b = 10;
//cout << add(a, b) << endl;编译器报错
}
而显示实例化是先转化成一致类型,然后进行操作,如下图编译后正常运行
template <typename T1>
T1 add(T1 a, T1 b) {
return a + b;
}
void test1() {
double a = 10.087;
int b = 10;
cout << add<int>(a, b) << endl;
cout << add<double>(a, 'a') << endl;
}
(2)声明了模板函数,至少要使用一次,不然会报错,如下图啥也没写也报错
template <typename T1,typename T2>
int main() {
return 0;
}
c++函数模板重载
函数模板可以重载,跟函数重载差不多
template <typename T1, class T2>
T1 add(T1 a, T2 b) {
return a + b;
}
template <typename T1, class T2,typename T3>
T1 add(T1 a, T2 b, T3 c) {
return a + b + c;
}
void test1() {
double a = 10.087;
int b = 10;
char c = 'a';
cout << add<int>(a, b) << endl;
cout << add(a, b, c) << endl;
}
c++ 普通函数和模板函数调用规则
(1)如果普通函数和模板函数都能实现,一般调用普通函数。
template <typename T1>
T1 add(T1 a, T1 b) {
cout << "调用模板函数" << endl;
return a + b;
}
int add(int a, int b) {
cout << "调用普通函数" << endl;
return a + b;
}
void test() {
int a = 10;
int b = 10;
cout << add(a, b) << endl;//会优先调用普通函数
}
若想要调用模板参数,则需要添加<>
cout << add<>(a, b) << endl;
(2)若模板函数优于普通函数,则调用模板函数。如下:
T1 add(T1 a, T1 b) {
cout << "调用模板函数" << endl;
return a + b;
}
int add(int a, int b) {
cout << "调用普通函数" << endl;
return a + b;
}
void test() {
double a = 3.124;
double b = 6.454;
cout << add(a, b) << endl;
}
这时,声明了两个double型变量,若调用普通函数则需要发生类型转换,所以编译器会优先调用模板函数减少工作量。
总之,编译器也会选择最优解,这个要稍微注意一下
c++函数模板显示具体化
有时候模板并不是万能的,比如声明了如下模板
template <typename T>
void equal(T& a, T& b) {
if (a == b) {
cout << "相等" << endl;
}
else {
cout << "不等" << endl;
}
}
这时比较俩个基本数据类型是ok的,但是要比较对象以及结构体这显然是行不通的,这时就需要函数模板显示具体化,函数模板显示具体化是对函数模板的重新定义,具体格式如下
template <> 返回值类型 函数名<实例化类型>(参数列表){
//函数体重新定义
}
下面是比较对象的例子
class Person {
public:
string name;
int age;
public:
Person(string name, int age);
};
template <typename T>
void equal(T& a, T& b) {
if (a == b) {
cout << "相等" << endl;
}
else {
cout << "不等" << endl;
}
}
//显示具体化,对函数模板重新定义
template <>void equal<Person>(Person& p1, Person& p2) {
if (p1.name == p2.name && p1.age == p2.age) {
cout << "相等1" << endl;
}
else {
cout << "不等0" << endl;
}
}
void test() {
Person p1("张三", 19);
Person p2("张三", 19);
equal(p1, p2);
}
上图写了Person类,比较两个数的模板函数,以及重新定义了函数模板,实现了比较对象属性的功能。这个稍微了解下。
c++ 类模板
类模板是针对成员数据类型不同的类的抽象,它不是一个具体实际的类,一个类模板可以生成多种具体的类。类模板的定义格式如下所示
template<typename 类型占位符>
class 类名{};
一旦声明类模板,就可以用类模板的参数名声明类中的成员变量和成员函数,即在类中使用数据类型的地方都可以使用模板参数名来声明,示例
template<typename T1,typename T2>
class A{
public:
T1 a;
T2 b;
A(T1 a,T2 b);
};
如果用类模板创建类A的对象 ,需要在类模板后面加上一个<>,并在里面表明相应的类型,如
A<string int> a;
需要注意的是:使用类模板时,必须要为模板参数显示指定实参,不存在实参推演过程,这一点与函数模板不同。简单示例:
template<typename T_Name, typename T_Age>
class Person {
public:
T_Name name;
T_Age age;
public:
Person(T_Name name, T_Age age);
};
类模板外定义函数
template<typename T_Name, typename T_Age>//类模板外定义成员函数
Person<T_Name, T_Age>::Person(T_Name name, T_Age age) {
this->name = name;
this->age = age;
}
创建类模板对象
void test() {
Person<string, int> person("张三", 19);//创建类模板的对象
}
需要注意的是,类模板在实例化时,带有模板参数的成员函数并不会跟着实例化,这些成员函数只有被调用时才会实例化。
c++ 类模板对象做函数参数
(1)直接显示对象所包含的数据类型,这种常用
//直接显示对象所包含的数据类型<strning,int>
void test1(Person<string, int>& person) {
person.showPerson();
}
void test2() {
Person<string, int> person("张三", 19);
test1(person);
}
(2)用模板显示对象所包含的数据类型,可以用typeid(类型占位符).name()查看什么类型
template<typename T1,typename T2>
void test1(Person<T1, T2>& person) {
person.showPerson();
cout << "查看T1类型:" << typeid(T1).name() << endl;
cout << "查看T2类型:" << typeid(T2).name() << endl;
}
(3)类模板化
template<typename T1>
void test1(T1& person) {
person.showPerson();
cout << "查看T1类型:" << typeid(T1).name() << endl;
}
c++ 类模板的派生类
(1)类模板派生普通类
在派生过程中,类模板先实例化出一个模板类,这个模板类作为基类派生出普通类
template<typename T_Name, typename T_Age>
class Person {
public:
T_Name name;
T_Age age;
};
//派生普通类
class American :public Person<string, int> {};
类模板Person派生出了普通类American,其实在这个派生过程中类模板Person先实例化出了一个Person<string,int>类型的模板,然后由这个模板类派生出普通类American,因此在派生过程中需要指定模板参数类型
(2)类模板派生类模板
如果基类模板由模板参数确定,而不是基本的string,int等,这时就要将派生类也声明为一个派生类,派生类模板的模板参数受基类模板的模板参数影响。如下:
声明Person基类模板
template<typename T_Name, typename T_Age>
class Person {
public:
T_Name name;
T_Age age;
public:
Person(T_Name name, T_Age age);
void showPerson();
};
派生类模板American继承基类模板Person
template<typename T_Name, typename T_Age>
class American:public Person<T_Name, T_Age> {
public:
American(T_Name name,T_Age age);
};
American类模板外实现构造函数为基类模板成员赋值
template<typename T_Name, typename T_Age>
American<T_Name,T_Age>::American(T_Name name, T_Age age):Person<T_Name, T_Age>(name, age) {}
调用函数测试:
void test3() {
American<string,int> american("李四", 19);
american.showPerson();
}
这里省略了一些不重要的代码,主要学的语法。
//如果想要分文件编写,将.h和.cpp文件写到一块并更改后缀名为.hpp的文件
c++ 类模板与友元函数
(1)非模板友元函数(类内实现)
非模板友元函数就是将一个普通函数声明为友元函数,如f2()为普通函数,则f2()时类模板Person所有实例的友元函数,注意:f1()不是函数模板,只是有一个模板类参数,调用带有模板类参数的友元函数时,需指明友元函数要引用的参数类型
template<typename T_Age>
class Person {
friend void f1(Person<T_Age>& person) {
cout << person.age << endl;
};
friend void f2();
public:
Person(T_Age age);
public:
T_Age age;
};
void f2() {}
void test() {
Person<int>person(10);
f1(person);//int型
Person<double>person1(20);
f1(person1);//double型
}
(2)(类外实现)
首先要在类模板定义的前面声明函数,还要声明所在类,如:
template<typename T_Age>
class Person;
template<typename T_Age>
void f1(Person<T_Age>& person);
template<typename T_Age>
void f2();
然后类中友元函数也后面要加<> ,如果没有用到模板参数时加要在<>里加上模板参数
template<typename T_Age>
class Person {
friend void f1<>(Person<T_Age> &person);//后面加<>
friend void f2<T_Age>();//没有用到模板参数时加要在<>里加上模板参数
public:
Person(T_Age age);
public:
T_Age age;
};
然后类外实现,简直又臭又长。。。
template<typename T_Age>
Person<T_Age>::Person(T_Age age) {
this->age = age;
}
template<typename T_Age>
void f1(Person<T_Age> &person) {
cout << person.age << endl;
}
void f2() {}
void test() {
Person<int>person(10);
f1(person);
Person<double>person1(20);
f1(person1);
}