您现在的位置是:首页 >技术杂谈 >c++ 模板网站首页技术杂谈

c++ 模板

u忧u 2024-06-17 10:25:02
简介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);
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。